SpannableString: qué es, clases de estilo y cómo funciona

Autor: IT Sectr Publicado: 2026-07-23 Tiempo de lectura: 10 min

SpannableString es una clase de Android SDK del paquete android.text que permite aplicar múltiples estilos a diferentes partes de una misma cadena de texto en TextView. A diferencia del marcado HTML, SpannableString trabaja a nivel de objetos Span, controlando la visualización del texto: color, tamaño, tipo de letra, subrayado y elementos interactivos. Según Google Developers, SpannableString se utiliza en componentes del sistema Android para formatear enlaces. Es la forma principal de estilizar texto sin usar bibliotecas de terceros.

Puntos Clave

  • SpannableString — una clase de Android para estilizar texto mediante objetos Span, inmutable después de su creación
  • Los Spans se dividen en CharacterStyle (caracteres individuales) y ParagraphStyle (párrafos completos)
  • ForegroundColorSpan cambia el color del texto, StyleSpan controla negrita y cursiva
  • SpannableStringBuilder — una versión mutable para construcción dinámica de texto con soporte Editable
  • Los flags de Span determinan el comportamiento del span al insertar o eliminar texto en los límites del rango

¿Qué es SpannableString?

SpannableString es una clase de Android que implementa la interfaz Spannable, almacenando texto junto con un conjunto de objetos Span que controlan la visualización. A diferencia de una String normal, SpannableString permite adjuntar atributos de estilo a rangos específicos de caracteres: hacer que parte del texto sea roja, aumentar el tamaño de fuente en un encabezado o agregar un enlace clickeable dentro de un párrafo.

La clase se encuentra en el paquete android.text y está disponible desde API Level 1. SpannableString es inmutable — una vez creado, su estructura queda fija y reemplazar texto requiere crear un nuevo objeto. Para edición dinámica se usa SpannableStringBuilder, que admite inserción y eliminación de caracteres sin perder estilos.

Diferencia de CharSequence

CharSequence es la interfaz base para datos de texto, implementada por String, StringBuilder y SpannableString. La principal diferencia entre SpannableString y String es el soporte para adjuntar objetos arbitrarios a una subcadena. TextView reconoce la interfaz Spannable y aplica los objetos span a los segmentos de texto correspondientes durante el renderizado. Si pasas una String normal a TextView, no se aplicará ningún estilo.

Estructura Interna

SpannableString almacena el texto como un array char[] y un array separado de objetos span con metadatos sobre las posiciones de inicio y fin. Al llamar a setSpan(what, start, end, flags), el objeto what se guarda en la lista junto con la información del rango. Durante el renderizado, TextView aplica secuencialmente todos los spans que caen dentro del rango mostrado, llamando a los métodos updateDrawState y updateMeasureState.

Tipos de Spans: CharacterStyle y ParagraphStyle

CharacterStyle es la clase base para spans que afectan a caracteres individuales independientemente de su posición en las líneas. Esto incluye ForegroundColorSpan (color de texto), RelativeSizeSpan (tamaño relativo), StyleSpan (negrita y cursiva), UnderlineSpan (subrayado) y otros. Los spans de caracteres se aplican a cada carácter en el rango especificado individualmente.

ParagraphStyle es una interfaz para spans que afectan a párrafos completos. El representante más conocido es AlignmentSpan, que alinea todo el párrafo a la izquierda, centro o derecha. Los spans de párrafo deben cubrir el párrafo completo; de lo contrario, Android ignora su aplicación. Esta limitación se debe a que la alineación o la sangría solo tienen sentido para un bloque de texto completo.

Flags de Span

Los flags de Span son cuatro constantes que definen el comportamiento del span al insertar o eliminar texto en los límites de su rango. SPAN_EXCLUSIVE_EXCLUSIVE mantiene el span activo solo dentro de los límites originales, SPAN_INCLUSIVE_INCLUSIVE lo extiende al agregar texto en los límites. SPAN_EXCLUSIVE_INCLUSIVE y SPAN_INCLUSIVE_EXCLUSIVE proporcionan un comportamiento mixto para el inicio y el final del rango.

FlagInserción a la IzquierdaInserción a la Derecha
SPAN_EXCLUSIVE_EXCLUSIVEno incluyeno incluye
SPAN_INCLUSIVE_INCLUSIVEincluyeincluye
SPAN_EXCLUSIVE_INCLUSIVEno incluyeincluye
SPAN_INCLUSIVE_EXCLUSIVEincluyeno incluye

Elegir el flag correcto es crítico para texto Editable en EditText, donde el usuario puede insertar y eliminar caracteres. Para TextView de solo lectura, normalmente se usa SPAN_EXCLUSIVE_EXCLUSIVE — el estilo se aplica solo al rango original y no se expande con cambios programáticos.

Clases Principales de Spans en Android SDK

El SDK de Android proporciona más de 25 clases de span incorporadas que cubren la mayoría de las tareas de estilo de texto. Cada clase implementa la interfaz CharacterStyle o ParagraphStyle y acepta parámetros a través de su constructor. Todas las clases están en el paquete android.text.style y están disponibles sin añadir dependencias adicionales.

Spans de Color y Fondo

ForegroundColorSpan establece el color del texto para un rango especificado, aceptando un color en formato int. BackgroundColorSpan pinta el fondo detrás del texto, útil para resaltar resultados de búsqueda. AbsoluteSizeSpan establece un tamaño de fuente exacto en píxeles, RelativeSizeSpan aplica un multiplicador relativo al tamaño base del texto en TextView.

Spans de Tipografía

StyleSpan acepta las constantes Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC o BOLD_ITALIC y cambia el tipo de letra de los caracteres. UnderlineSpan agrega subrayado, StrikethroughSpan agrega tachado. SuperscriptSpan y SubscriptSpan crean superíndice y subíndice. TypefaceSpan permite establecer una fuente personalizada mediante un objeto Typeface para un rango de texto.

Spans Interactivos

ClickableSpan es una clase abstracta para crear segmentos de texto clickeables. Al tocarlo, se llama al método onClick(). Para que los clics funcionen, TextView debe tener setMovementMethod(LinkMovementMethod.getInstance()). URLSpan es una subclase de ClickableSpan para hipervínculos con apertura automática del navegador. ClickableSpan a menudo se combina con ForegroundColorSpan para resaltar visualmente el enlace en azul.

kotlin
val spannable = SpannableString("Open developer.android.com")
spannable.setSpan(
    URLSpan("https://developer.android.com"),
    9, 31, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
spannable.setSpan(
    ForegroundColorSpan(Color.BLUE),
    9, 31, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
textView.text = spannable
textView.movementMethod = LinkMovementMethod.getInstance()

Sin LinkMovementMethod, los clics en URLSpan no se procesarán. MovementMethod se encarga de interceptar eventos táctiles y encontrar el ClickableSpan en la posición del toque. El span de color hace visible el enlace para el usuario.

Cómo Usar SpannableString en Código

Trabajar con SpannableString comienza creando una instancia a partir de una cadena de texto y aplicando spans secuencialmente mediante el método setSpan(). El método acepta cuatro parámetros: el objeto span, la posición inicial, la posición final y los flags. Después de establecer todos los spans, el objeto se pasa a TextView mediante setText().

Color y Tamaño del Texto

Creemos una cadena donde la primera palabra sea roja y de mayor tamaño. Para esto, usamos ForegroundColorSpan para el color y RelativeSizeSpan para la escala. Ambos spans se aplican al mismo rango de forma independiente — el orden de las llamadas a setSpan no importa.

kotlin
val text = "Header: remaining text"
val spannable = SpannableString(text)
val colorSpan = ForegroundColorSpan(Color.RED)
val sizeSpan = RelativeSizeSpan(1.5f)
spannable.setSpan(colorSpan, 0, 9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(sizeSpan, 0, 9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannable

Los caracteres desde la posición 0 hasta la 9 reciben ambos estilos simultáneamente. TextView aplica automáticamente todos los spans durante el renderizado — no se necesitan llamadas adicionales. RelativeSizeSpan con un multiplicador de 1.5f aumenta el tamaño de fuente en un 50% respecto al base.

Combinación con Etiquetas HTML

Html.fromHtml() crea un objeto Spanned a partir de una cadena HTML, pero el conjunto de etiquetas soportadas es limitado. SpannableString da control total sobre cada atributo sin limitaciones de HTML. Si necesitas convertir HTML a spans y luego agregar estilos personalizados, puedes usar Html.fromHtml() como base y luego complementar con spans mediante setSpan().

kotlin
val htmlText = Html.fromHtml(
    "<b>Important:</b> check the data",
    Html.FROM_HTML_MODE_LEGACY
)
val spannable = SpannableString(htmlText)
spannable.setSpan(
    ForegroundColorSpan(Color.RED),
    0, 6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
textView.text = spannable

El resultado — el texto “Importante:” estará en negrita (desde HTML) y en rojo (desde el span). Este enfoque es conveniente al trabajar con contenido del servidor donde parte del formato se especifica en HTML y otra parte se añade del lado del cliente mediante programación.

Combinación de Múltiples Spans

SpannableString permite aplicar una cantidad ilimitada de spans al mismo rango o a rangos superpuestos. La combinación de spans es una ventaja clave sobre el marcado HTML, donde las etiquetas anidadas pueden entrar en conflicto. Los spans son independientes y se aplican secuencialmente durante el renderizado.

Por ejemplo, puedes hacer que un segmento de texto sea simultáneamente negrita, rojo y clickeable. Para ello, crea tres spans — StyleSpan, ForegroundColorSpan y ClickableSpan — y aplica cada uno al mismo rango. El orden de aplicación no afecta el resultado, ya que cada span es responsable de su propio atributo de texto.

Superposición de Spans de Diferentes Tipos

Si los spans de diferentes tipos se superponen parcialmente, cada uno funciona independientemente dentro de sus propios límites. ForegroundColorSpan en el rango 0–10 y StyleSpan(BOLD) en el rango 5–15 darán texto rojo en negrita en el segmento 5–10 y solo negrita en el 10–15. No surgen conflictos porque cada span modifica su propio atributo durante el renderizado.

Obtener la Lista de Spans

El método getSpans(int start, int end, Class type) devuelve un array de spans dentro del rango especificado. Esto es útil para comprobar qué estilos ya se han aplicado o para eliminar spans específicos. Con nextSpanTransition(), puedes iterar sobre los límites de cambio de spans — esta es la base para implementaciones personalizadas de TextView que necesitan saber dónde cambia el estilo.

SpannableStringBuilder para Construcción de Texto

SpannableStringBuilder es una clase para la construcción paso a paso de texto con estilo, con la capacidad de insertar, reemplazar y eliminar fragmentos. A diferencia de SpannableString, que se crea a partir de una cadena ya preparada y es inmutable, Builder permite agregar partes de texto secuencialmente y asignar estilos sobre la marcha. Es ideal para mensajes compuestos: registros, chats, titulares de noticias con etiquetas dinámicas.

Builder implementa las interfaces Spannable y Editable, lo que lo hace compatible con EditText. El usuario puede editar el texto, y los estilos se conservan y se desplazan correctamente al insertar nuevos caracteres. SpannableString, al ser inmutable, no es adecuado para campos editables.

Encadenamiento de Métodos

El método append() devuelve el propio builder, lo que permite el encadenamiento de métodos. Después de agregar texto, los spans se aplican mediante setSpan(). Las posiciones se especifican en relación con la longitud actual del builder. También están disponibles Insert() y replace() para un control más preciso del contenido.

kotlin
val builder = SpannableStringBuilder()
    .append("New ")
    .append("comment")
val blue = ForegroundColorSpan(Color.BLUE)
val gray = ForegroundColorSpan(Color.GRAY)
builder.setSpan(blue, 0, 6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
builder.setSpan(gray, 6, 17, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = builder

El texto “Nuevo ” está coloreado en azul, y “comentario” en gris. Al insertar caracteres adicionales entre ellos, el span no afectará al nuevo texto gracias a los flags EXCLUSIVE. Builder ajusta automáticamente los índices internos al modificarse.

Rendimiento y Optimización de Spans

El uso de una gran cantidad de spans afecta el rendimiento de renderizado de TextView. Cada span llama al método updateDrawState() o updateMeasureState() en cada redibujado. Se recomienda limitar la cantidad de spans por TextView a 50–100 para un rendimiento cómodo en dispositivos de gama media. Los spans que cambian el tamaño del texto (RelativeSizeSpan, AbsoluteSizeSpan) requieren recálculo del layout en cada cambio, lo que es significativamente más costoso que los spans de solo color o subrayado.

TextAppearanceSpan

TextAppearanceSpan permite aplicar un conjunto completo de estilos desde un recurso XML android:textAppearance con una sola llamada a setSpan(). En lugar de tres spans separados (color, tamaño, fuente), se usa un solo TextAppearanceSpan con una referencia al estilo. Esto reduce la cantidad de objetos y simplifica el mantenimiento — cambiar el estilo en el recurso se aplica automáticamente a todos los textos que usan este span.

Reutilización de Objetos

Crear una nueva instancia de span para cada setSpan() genera carga adicional en el recolector de basura. Es óptimo crear objetos span constantes si se usan repetidamente. Por ejemplo, ForegroundColorSpan(Color.RED) se puede almacenar en un objeto companion y reutilizar. Sin embargo, los spans con estado (ClickableSpan con diferentes manejadores) deben crearse individualmente para cada caso.

Medición y Perfilado

Para diagnosticar problemas de rendimiento con spans, usa Layout Inspector en Android Studio y el perfilador de GPU. Si un TextView con muchos spans se ralentiza notablemente al hacer scroll, considera reemplazar algunos spans con estilos estáticos mediante TextAppearanceSpan o reducir la cantidad de spans combinando atributos en implementaciones personalizadas de UpdateAppearance.

Preguntas Frecuentes

¿En qué se diferencia SpannableString de una String normal?

String es una secuencia inmutable de caracteres sin soporte de estilos. SpannableString almacena los mismos caracteres pero además contiene un array de objetos Span con información de formato. TextView determina el tipo de CharSequence pasado y aplica los spans a los rangos correspondientes durante el renderizado. String ignora cualquier atributo de estilo y se muestra como texto plano.

¿Cómo eliminar todos los spans de SpannableString?

El método removeSpan(Object span) elimina un span específico. Para una limpieza completa, llama a getSpans(0, length, Object::class.java), que devuelve un array de todos los spans, y luego elimina cada uno mediante removeSpan. Alternativamente, crea un nuevo SpannableString(text.toString()) sin spans. SpannableStringBuilder tiene un método clear() que elimina tanto el texto como los spans.

¿Funciona SpannableString en EditText?

SpannableString funciona en EditText, pero para texto editable es preferible SpannableStringBuilder, que implementa Editable. EditText requiere la interfaz Editable para rastrear cambios. Si pasas SpannableString a EditText, el texto se mostrará con estilos, pero durante la edición Android lo convertirá a Editable, lo que puede restablecer algunos spans.

¿Se puede usar SpannableString en Compose?

En Jetpack Compose, los spans de Android SDK no se usan directamente. En su lugar, Compose proporciona AnnotatedString, su propio equivalente de SpannableString con capacidades similares: SpanStyle para estilos de caracteres individuales y ParagraphStyle para párrafos. La conversión de SpannableString a AnnotatedString es posible mediante buildAnnotatedString con iteración sobre los spans.

¿Cómo crear un Span personalizado?

Crea una clase que extienda CharacterStyle y sobrescribe el método updateDrawState(TextPaint tp). Dentro del método, modifica las propiedades de TextPaint: color, grosor de línea, efectos. Para cambios métricos, usa UpdateLayout o MetricAffectingSpan. Los spans personalizados se aplican mediante setSpan() igual que los incorporados.

Resumen

  • SpannableString — una clase de Android para estilizar texto mediante objetos Span con vinculación a rangos de caracteres
  • CharacterStyle afecta a caracteres individuales, ParagraphStyle afecta a párrafos completos
  • Spans principales: ForegroundColorSpan, StyleSpan, RelativeSizeSpan, URLSpan, ClickableSpan
  • SpannableStringBuilder — una versión mutable para construcción dinámica de texto con Editable
  • Combinar spans es seguro — cada span cambia su propio atributo de forma independiente
  • Los flags de Span controlan el comportamiento del span al insertar texto en los límites
  • Optimización: usa TextAppearanceSpan para grupos de estilo y limita los spans a 100

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también