TextMeasurer: o que é e como funciona a medição de texto no Compose

Autor: IT Sectr Publicado: 2026-07-25 Tempo de leitura: 8 min

TextMeasurer é uma API do Jetpack Compose projetada para medir as dimensões do texto antes de sua renderização real na tela. Diferente da abordagem clássica via Paint.measureText no Android View, o TextMeasurer fornece uma forma nativa do Compose de obter largura, altura e métricas de linhas, levando em conta o estilo e as restrições. De acordo com a Android Developers Documentation (2025), o TextMeasurer é ativamente usado em componentes Layout personalizados, rótulos animados e elementos de UI adaptativos onde o tamanho do texto afeta o posicionamento de outros elementos.

Pontos Principais

  • TextMeasurer — Uma API do Compose para medir texto antes da renderização.
  • O método measure() retorna TextLayoutResult com métricas de linhas e caracteres.
  • A API considera estilo, fonte, tamanho e restrições do contêiner em pixels.
  • TextMeasurer suporta medição de linha única e texto multilinha.
  • O resultado da medição é usado para layout personalizado e animação de texto.

O que é TextMeasurer no Jetpack Compose?

TextMeasurer é uma interface funcional do pacote androidx.compose.ui.text que permite medir texto no Compose sem realmente renderizá-lo na tela. Faz parte do motor de texto do Compose e opera no nível da Paragraph API, que usa Skia para renderização.

O principal propósito do TextMeasurer é a pré-medição. No Compose, o tamanho do texto geralmente é determinado depois que o Composable já está colocado no Layout. No entanto, existem cenários onde é preciso saber a largura do texto antes da composição — por exemplo, para decidir se uma string cabe em um contêiner, ou para calcular o tamanho de um botão com base no comprimento do texto.

TextMeasurer resolve isso fornecendo o método measure(), que recebe um TextMeasureRequest (texto, estilo, restrições) e retorna TextLayoutResult (métricas, linhas, caracteres). Isso permite que o desenvolvedor obtenha todos os dados do texto de forma síncrona, sem atraso de um quadro.

Use TextMeasurer quando o tamanho do texto afetar o tamanho do componente pai, ao criar transições de texto animadas ou ao implementar Layouts personalizados onde o texto coexiste com outros elementos.

Como o TextMeasurer funciona

A API TextMeasurer consiste em duas entidades principais: rememberTextMeasurer() (criação de instância) e measure() (execução da medição). A instância é criada uma vez para todo o ciclo de vida do componente e reutilizada para todas as medições.

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

O parâmetro constraints define a largura e altura máximas dentro das quais o texto deve caber. Se o texto não couber na largura, ele quebra para a próxima linha. O resultado da medição contém um TextLayoutResult, que fornece o número de linhas, a posição de cada caractere, a largura de cada linha e o tamanho total do texto.

TextMeasurer funciona de forma síncrona na thread de UI e não desencadeia recomposição. No entanto, a medição frequente de texto com estilos diferentes pode criar sobrecarga — portanto, a instância é criada via remember e reutilizada.

Medição de texto de linha única

O cenário mais simples é medir a largura de texto de linha única para determinar o tamanho do contêiner pai. Por exemplo, para um botão cuja largura depende do comprimento do rótulo.

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)
}

Neste exemplo, remember(label) garante que a medição reinicie apenas quando o texto mudar. As restrições são definidas com um maxWidth grande (1000) para evitar quebra de linha — a restrição real será igual à largura do texto. A largura do Box é definida como a largura medida do texto, resultando em um efeito de “botão ajustado ao texto”.

Nota: a largura do resultado precisa ser convertida para dp usando .dp, já que TextMeasurer retorna dimensões em pixels, enquanto Modifier.width espera Dp. Para precisão, use density e LocalDensity.current.

Medição de texto multilinha

Para texto multilinha, o TextMeasurer permite determinar não apenas a altura total, mas também o número de linhas e a posição de cada caractere dentro do parágrafo. Isso é necessário ao criar campos de texto personalizados, chats ou editores.

As principais propriedades do TextLayoutResult para texto multilinha: lineCount (número de linhas), getLineTop(index) (coordenada Y do início da linha), getLineBottom(index) (coordenada Y do final da linha), getLineWidth(index) (largura da linha). Combinando-as, você pode posicionar com precisão elementos de interface em relação às linhas 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

O método getLineWidth(lineIndex) retorna a largura da linha especificada em pixels. Pode ser menor que maxWidth se a linha não atingir o limite do contêiner. Conhecendo a largura de cada linha, você pode implementar um efeito de justificação manualmente ou colocar elementos inline ao lado de linhas curtas.

Para obter a posição de um caractere específico, use getBoundingBox(offset), que retorna um Rect com coordenadas left, top, right, bottom. Isso é útil para posicionar o cursor em campos de texto personalizados ou para destacar um intervalo de texto com animação.

TextMeasurer em componentes personalizados

A aplicação mais poderosa do TextMeasurer são os componentes Layout personalizados, onde o texto coexiste com outros elementos e seus tamanhos são interdependentes. Por exemplo, um widget “Ícone + texto” onde o ícone deve ser centralizado em relação à primeira linha de texto, não ao bloco inteiro.

Em um Row padrão, o ícone é centralizado por toda a altura do Row, o que parece pouco natural quando o texto ocupa 3 linhas e o ícone apenas uma. Com TextMeasurer, você pode medir a primeira linha e alinhar o ícone precisamente a ela.

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)
        }
    }
)

Neste exemplo, medir a primeira linha via TextMeasurer determina a posição do ícone. Sem TextMeasurer, o ícone seria centralizado por toda a altura do texto multilinha, o que é visualmente incorreto. Este padrão é amplamente usado em chats, listas de contatos e cartões de informação.

Dicas de desempenho

TextMeasurer é uma API síncrona executada na thread de UI. O uso excessivo pode causar queda de quadros, especialmente ao medir grandes volumes de texto em dispositivos fracos.

Primeira regra — armazene o resultado em cache. Sempre envolva a chamada measure() em remember com dependências de texto e estilo. Nunca chame measure() dentro de CompositionLocal ou em caminhos de recomposição quentes.

Segunda regra — limite maxWidth e maxHeight. Se você definir maxWidth = Constraints.Infinity, o TextMeasurer não conseguirá quebrar linhas e o texto sairá da tela. Sempre passe restrições realistas, especialmente para texto multilinha.

Terceira regra — evite medição repetida com os mesmos parâmetros. Se o texto e o estilo não mudaram, o resultado de measure() será o mesmo. Combine medição com Deferred ou LaunchedEffect para cenários assíncronos.

Perguntas Frequentes

Como o TextMeasurer difere do onSizeChanged no Compose?

TextMeasurer mede o texto antes da renderização, enquanto onSizeChanged mede depois. TextMeasurer é necessário quando o tamanho do texto afeta o layout pai; onSizeChanged funciona para lógica reativa após a composição.

Pode-se medir SpannableString com TextMeasurer?

Sim, TextMeasurer.measure() aceita AnnotatedString, que inclui SpannableString com estilos inline, links e cores. Todos os spans são considerados durante a medição.

Por que o TextMeasurer retorna dimensões em pixels em vez de dp?

TextMeasurer opera no nível de renderização Skia, onde todas as dimensões estão em pixels. Converta via LocalDensity.current: density.run { width.toDp() }.

Como medir texto antes de uma fonte ser carregada?

TextMeasurer usa FontFamily.Default até que uma fonte personalizada seja carregada. Para medição precisa, aguarde o carregamento da fonte via FontResource e use async/await.

O TextMeasurer afeta o desempenho de listas LazyColumn?

Medir texto em cada item do LazyColumn pode reduzir FPS em dispositivos fracos. Otimização: armazene em cache o resultado via remember e limite o tamanho do texto.

Resumo

  • TextMeasurer — Uma API do Compose para medir texto antes da renderização, retorna TextLayoutResult.
  • O método measure() aceita texto, estilo e Constraints em pixels.
  • A instância é criada via rememberTextMeasurer() e reutilizada.
  • Para texto de linha única, o resultado é usado para largura adaptativa do contêiner.
  • Para texto multilinha — lineCount, getLineWidth() e getBoundingBox() fornecem a imagem completa.
  • Em Layouts personalizados, TextMeasurer permite alinhar elementos pela primeira linha de texto.
  • Armazene o resultado em cache via remember e evite medir em caminhos de recomposição quentes.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também