TextMeasurer: qué es y cómo funciona la medición de texto en Compose

Autor: IT Sectr Publicado: 2026-07-25 Tiempo de lectura: 8 min

TextMeasurer es una API de Jetpack Compose diseñada para medir las dimensiones del texto antes de que se renderice realmente en pantalla. A diferencia del enfoque clásico mediante Paint.measureText en Android View, TextMeasurer proporciona una forma nativa de Compose para obtener el ancho, la altura y las métricas de las líneas teniendo en cuenta el estilo y las restricciones. Según Android Developers Documentation (2025), TextMeasurer se utiliza activamente en componentes Layout personalizados, etiquetas animadas y elementos de UI adaptativos donde el tamaño del texto afecta el posicionamiento de otros elementos.

Puntos Clave

  • TextMeasurer — Una API de Compose para medir texto antes de renderizar.
  • El método measure() devuelve TextLayoutResult con métricas de líneas y caracteres.
  • La API considera estilo, fuente, tamaño y restricciones del contenedor en píxeles.
  • TextMeasurer admite medir una sola línea y texto multilínea.
  • El resultado de la medición se usa para diseño personalizado y animación de texto.

¿Qué es TextMeasurer en Jetpack Compose?

TextMeasurer es una interfaz funcional del paquete androidx.compose.ui.text que permite medir texto en Compose sin renderizarlo realmente en pantalla. Es parte del motor de texto de Compose y opera a nivel de Paragraph API, que utiliza Skia para el renderizado.

El propósito principal de TextMeasurer es la pre-medición. En Compose, el tamaño del texto generalmente se determina después de que el Composable ya está colocado en el Layout. Sin embargo, hay escenarios donde se necesita conocer el ancho del texto antes de la composición — por ejemplo, para decidir si una cadena cabe en un contenedor, o para calcular el tamaño de un botón según la longitud del texto.

TextMeasurer resuelve esto proporcionando el método measure(), que toma un TextMeasureRequest (texto, estilo, restricciones) y devuelve TextLayoutResult (métricas, líneas, caracteres). Esto permite al desarrollador obtener todos los datos del texto de forma síncrona, sin un retraso de un fotograma.

Usa TextMeasurer cuando el tamaño del texto afecte el tamaño del componente padre, al crear transiciones de texto animadas o al implementar Layouts personalizados donde el texto coexista con otros elementos.

Cómo funciona TextMeasurer

La API de TextMeasurer consta de dos entidades clave: rememberTextMeasurer() (creación de una instancia) y measure() (ejecución de la medición). La instancia se crea una vez para todo el ciclo de vida del componente y se reutiliza para todas las mediciones.

kotlin
val textMeasurer = rememberTextMeasurer()

val result = textMeasurer.measure(
    text = "Hello, Compose!",
    style = MaterialTheme.typography.bodyLarge,
    constraints = Constraints(maxWidth = 200, maxHeight = 50)
)

// result.size.width, result.size.height, result.lineCount

El parámetro constraints define el ancho y alto máximo dentro del cual debe caber el texto. Si el texto no cabe en el ancho, se envuelve a la siguiente línea. El resultado de la medición contiene un TextLayoutResult, que proporciona el número de líneas, la posición de cada carácter, el ancho de cada línea y el tamaño total del texto.

TextMeasurer funciona de forma síncrona en el hilo de UI y no provoca recomposición. Sin embargo, la medición frecuente de texto con diferentes estilos puede generar sobrecarga — por lo tanto, la instancia se crea mediante remember y se reutiliza.

Medición de una sola línea de texto

El escenario más simple es medir el ancho de texto de una sola línea para determinar el tamaño del contenedor padre. Por ejemplo, para un botón cuyo ancho depende de la longitud de la etiqueta.

kotlin
val textMeasurer = rememberTextMeasurer()
var label by remember { mutableStateOf("Submit") }

val textResult = remember(label) {
    textMeasurer.measure(
        text = label,
        style = MaterialTheme.typography.labelLarge,
        constraints = Constraints(maxWidth = 1000, maxHeight = 100)
    )
}

Box(
    modifier = Modifier
        .width(textResult.size.width.dp)
        .height(40.dp)
        .background(Color.Blue)
        .clickable { onClick() }
) {
    Text(text = label, style = MaterialTheme.typography.labelLarge)
}

En este ejemplo, remember(label) asegura que la medición se reinicie solo cuando cambie el texto. Las restricciones se establecen con un maxWidth grande (1000) para evitar el ajuste de línea — la restricción real será igual al ancho del texto. El ancho del Box se establece al ancho medido del texto, lo que produce un efecto de “botón ajustado al texto”.

Nota: el ancho del resultado debe convertirse a dp usando .dp, ya que TextMeasurer devuelve dimensiones en píxeles, mientras que Modifier.width espera Dp. Para mayor precisión, usa density y LocalDensity.current.

Medición de texto multilínea

Para texto multilínea, TextMeasurer permite determinar no solo la altura total, sino también el número de líneas y la posición de cada carácter dentro del párrafo. Esto es necesario al crear campos de texto personalizados, chats o editores.

Las principales propiedades de TextLayoutResult para texto multilínea: lineCount (número de líneas), getLineTop(index) (coordenada Y del inicio de la línea), getLineBottom(index) (coordenada Y del final), getLineWidth(index) (ancho de línea). Combinándolas, puedes posicionar con precisión los elementos de la interfaz en relación con las líneas de texto.

kotlin
val textMeasurer = rememberTextMeasurer()
val result = textMeasurer.measure(
    text = "Long multiline text that needs to be measured before layout",
    style = MaterialTheme.typography.bodyMedium,
    constraints = Constraints(maxWidth = 150, maxHeight = 300)
)

val linesCount = result.lineCount
val firstLineWidth = result.getLineWidth(0)
val totalHeight = result.size.height

El método getLineWidth(lineIndex) devuelve el ancho de la línea especificada en píxeles. Puede ser menor que maxWidth si la línea no alcanza el borde del contenedor. Conociendo el ancho de cada línea, puedes implementar un efecto de justificación manualmente o colocar elementos inline junto a líneas cortas.

Para obtener la posición de un carácter específico, usa getBoundingBox(offset), que devuelve un Rect con las coordenadas left, top, right, bottom. Esto es útil para posicionar el cursor en campos de texto personalizados o para resaltar un rango de texto con animación.

TextMeasurer en componentes personalizados

La aplicación más potente de TextMeasurer son los componentes Layout personalizados, donde el texto coexiste con otros elementos y sus tamaños son interdependientes. Por ejemplo, un widget “Icono + texto” donde el icono debe centrarse con respecto a la primera línea de texto, no a todo el bloque.

En un Row estándar, el icono se centra por toda la altura del Row, lo que parece poco natural cuando el texto ocupa 3 líneas y el icono solo una. Con TextMeasurer, puedes medir la primera línea y alinear el icono precisamente con ella.

kotlin
val textMeasurer = rememberTextMeasurer()

Layout(
    content = {
        Icon(imageVector = Icons.Default.Star, contentDescription = null)
        Text(text = "Multi-line text here")
    },
    measurePolicy = { measurables, constraints ->
        val textMeasurable = measurables[1]
        val textPlaceable = textMeasurable.measure(constraints)
        val iconPlaceable = measurables[0].measure(constraints)

        val firstLineHeight = textMeasurer.measure(
            text = "Sample",
            style = MaterialTheme.typography.bodyMedium,
            constraints = Constraints(maxWidth = 2000, maxHeight = 100)
        ).size.height

        layout(width = textPlaceable.width + iconPlaceable.width,
               height = maxOf(textPlaceable.height, iconPlaceable.height)) {
            iconPlaceable.placeRelative(0, (firstLineHeight - iconPlaceable.height) / 2)
            textPlaceable.placeRelative(iconPlaceable.width, 0)
        }
    }
)

En este ejemplo, medir la primera línea mediante TextMeasurer determina la posición del icono. Sin TextMeasurer, el icono se centraría por toda la altura del texto multilínea, lo que es visualmente incorrecto. Este patrón se usa ampliamente en chats, listas de contactos y tarjetas de información.

Consejos de rendimiento

TextMeasurer es una API síncrona que se ejecuta en el hilo de UI. Un uso excesivo puede provocar caídas de fotogramas, especialmente al medir grandes volúmenes de texto en dispositivos débiles.

La primera regla — almacena en caché el resultado. Siempre envuelve la llamada a measure() en remember con dependencias de texto y estilo. Nunca llames a measure() dentro de CompositionLocal o en rutas de recomposición calientes.

La segunda regla — limita maxWidth y maxHeight. Si estableces maxWidth = Constraints.Infinity, TextMeasurer no podrá ajustar las líneas y el texto se saldrá de la pantalla. Siempre pasa restricciones realistas, especialmente para texto multilínea.

La tercera regla — evita mediciones repetidas con los mismos parámetros. Si el texto y el estilo no han cambiado, el resultado de measure() será el mismo. Combina la medición con Deferred o LaunchedEffect para escenarios asíncronos.

Preguntas Frecuentes

¿En qué se diferencia TextMeasurer de onSizeChanged en Compose?

TextMeasurer mide el texto antes de renderizar, mientras que onSizeChanged mide después. TextMeasurer es necesario cuando el tamaño del texto afecta al layout padre; onSizeChanged funciona para lógica reactiva después de la composición.

¿Se puede medir SpannableString con TextMeasurer?

Sí, TextMeasurer.measure() acepta AnnotatedString, que incluye SpannableString con estilos inline, enlaces y colores. Todos los spans se tienen en cuenta durante la medición.

¿Por qué TextMeasurer devuelve dimensiones en píxeles y no en dp?

TextMeasurer opera a nivel de renderizado Skia, donde todas las dimensiones están en píxeles. Convierte mediante LocalDensity.current: density.run { width.toDp() }.

¿Cómo medir texto antes de que se cargue una fuente?

TextMeasurer usa FontFamily.Default hasta que se carga una fuente personalizada. Para una medición precisa, espera a que la fuente se cargue mediante FontResource y usa async/await.

¿Afecta TextMeasurer al rendimiento de listas LazyColumn?

Medir texto en cada elemento de LazyColumn puede reducir los FPS en dispositivos débiles. Optimización: almacena en caché el resultado mediante remember y limita el tamaño del texto.

Resumen

  • TextMeasurer — Una API de Compose para medir texto antes de renderizar, devuelve TextLayoutResult.
  • El método measure() acepta texto, estilo y Constraints en píxeles.
  • La instancia se crea mediante rememberTextMeasurer() y se reutiliza.
  • Para texto de una sola línea, el resultado se usa para ancho adaptativo del contenedor.
  • Para texto multilínea — lineCount, getLineWidth() y getBoundingBox() ofrecen la imagen completa.
  • En Layouts personalizados, TextMeasurer permite alinear elementos por la primera línea de texto.
  • Almacena en caché el resultado mediante remember y evita medir en rutas de recomposición calientes.

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