TextMeasurer: cos’è e come funziona la misurazione del testo in Compose

Autore: IT Sectr Pubblicato: 2026-07-25 Tempo di lettura: 8 min

TextMeasurer è un’API di Jetpack Compose progettata per misurare le dimensioni del testo prima del suo effettivo rendering sullo schermo. A differenza dell’approccio classico tramite Paint.measureText in Android View, TextMeasurer fornisce un modo nativo di Compose per ottenere larghezza, altezza e metriche delle righe tenendo conto dello stile e dei vincoli. Secondo Android Developers Documentation (2025), TextMeasurer è attivamente utilizzato in componenti Layout personalizzati, etichette animate ed elementi UI adattivi dove la dimensione del testo influisce sul posizionamento di altri elementi.

Punti Chiave

  • TextMeasurer — Un’API Compose per misurare il testo prima del rendering.
  • Il metodo measure() restituisce TextLayoutResult con metriche di righe e caratteri.
  • L’API considera stile, carattere, dimensione e vincoli del contenitore in pixel.
  • TextMeasurer supporta la misurazione di una singola riga e di testo multilinea.
  • Il risultato della misurazione viene utilizzato per layout personalizzati e animazioni del testo.

Cos’è TextMeasurer in Jetpack Compose?

TextMeasurer è un’interfaccia funzionale del pacchetto androidx.compose.ui.text che consente di misurare il testo in Compose senza effettivamente renderizzarlo sullo schermo. Fa parte del motore di testo di Compose e opera a livello di Paragraph API, che utilizza Skia per il rendering.

Lo scopo principale di TextMeasurer è la pre-misurazione. In Compose, la dimensione del testo viene solitamente determinata dopo che il Composable è già stato posizionato nel Layout. Tuttavia, esistono scenari in cui è necessario conoscere la larghezza del testo prima del layout — ad esempio, per decidere se una stringa si adatta a un contenitore, o per calcolare la dimensione di un pulsante in base alla lunghezza del testo.

TextMeasurer risolve questo problema fornendo il metodo measure(), che accetta un TextMeasureRequest (testo, stile, vincoli) e restituisce TextLayoutResult (metriche, righe, caratteri). Ciò consente allo sviluppatore di ottenere tutti i dati del testo in modo sincrono, senza un ritardo di un fotogramma.

Utilizza TextMeasurer quando la dimensione del testo influisce sulla dimensione del componente padre, quando crei transizioni di testo animate o quando implementi Layout personalizzati in cui il testo coesiste con altri elementi.

Come funziona TextMeasurer

L’API TextMeasurer è composta da due entità chiave: rememberTextMeasurer() (creazione di un’istanza) e measure() (esecuzione della misurazione). L’istanza viene creata una volta per l’intero ciclo di vita del componente e viene riutilizzata per tutte le misurazioni.

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

Il parametro constraints definisce la larghezza e l’altezza massime entro cui il testo deve adattarsi. Se il testo non si adatta in larghezza, va a capo alla riga successiva. Il risultato della misurazione contiene un TextLayoutResult, che fornisce il numero di righe, la posizione di ogni carattere, la larghezza di ogni riga e la dimensione complessiva del testo.

TextMeasurer funziona in modo sincrono sul thread UI e non innesca la ricomposizione. Tuttavia, misurare frequentemente il testo con stili diversi può creare overhead — pertanto l’istanza viene creata tramite remember e riutilizzata.

Misurazione di una singola riga di testo

Lo scenario più semplice è misurare la larghezza di testo su una singola riga per determinare la dimensione del contenitore padre. Ad esempio, per un pulsante la cui larghezza dipende dalla lunghezza dell’etichetta.

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

In questo esempio, remember(label) garantisce che la misurazione si riavvii solo quando il testo cambia. I vincoli sono impostati con un maxWidth grande (1000) per evitare l’andata a capo — il vincolo reale sarà uguale alla larghezza del testo. La larghezza del Box viene impostata sulla larghezza misurata del testo, ottenendo un effetto di “pulsante adattato al testo”.

Nota: la larghezza dal risultato deve essere convertita in dp usando .dp, poiché TextMeasurer restituisce dimensioni in pixel, mentre Modifier.width si aspetta Dp. Per precisione, usa density e LocalDensity.current.

Misurazione di testo multilinea

Per il testo multilinea, TextMeasurer consente di determinare non solo l’altezza totale, ma anche il numero di righe e la posizione di ogni carattere all’interno del paragrafo. Ciò è necessario quando si creano campi di testo personalizzati, chat o editor.

Le principali proprietà di TextLayoutResult per il testo multilinea: lineCount (numero di righe), getLineTop(index) (coordinata Y dell’inizio riga), getLineBottom(index) (coordinata Y di fine riga), getLineWidth(index) (larghezza riga). Combinandole, puoi posizionare con precisione gli elementi dell’interfaccia rispetto alle righe di testo.

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

Il metodo getLineWidth(lineIndex) restituisce la larghezza della riga specificata in pixel. Può essere inferiore a maxWidth se la riga non raggiunge il limite del contenitore. Conoscendo la larghezza di ogni riga, puoi implementare manualmente un effetto di giustificazione o posizionare elementi inline accanto a righe corte.

Per ottenere la posizione di un carattere specifico, usa getBoundingBox(offset), che restituisce un Rect con coordinate left, top, right, bottom. Ciò è utile per posizionare il cursore in campi di testo personalizzati o per evidenziare un intervallo di testo con animazione.

TextMeasurer in componenti personalizzati

L’applicazione più potente di TextMeasurer sono i componenti Layout personalizzati, dove il testo coesiste con altri elementi e le loro dimensioni sono interdipendenti. Ad esempio, un widget “Icona + testo” in cui l’icona deve essere centrata rispetto alla prima riga di testo, non all’intero blocco.

In un Row standard, l’icona è centrata su tutta l’altezza del Row, il che appare innaturale quando il testo occupa 3 righe mentre l’icona è una sola. Con TextMeasurer, puoi misurare la prima riga e allineare l’icona precisamente ad essa.

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

In questo esempio, la misurazione della prima riga tramite TextMeasurer determina la posizione dell’icona. Senza TextMeasurer, l’icona verrebbe centrata sull’intera altezza del testo multilinea, il che è visivamente scorretto. Questo pattern è ampiamente utilizzato in chat, elenchi di contatti e schede informative.

Consigli sulle prestazioni

TextMeasurer è un’API sincrona che opera sul thread UI. Un uso eccessivo può causare cali di fotogrammi, specialmente quando si misurano grandi volumi di testo su dispositivi deboli.

Prima regola — memorizza nella cache il risultato. Avvolgi sempre la chiamata a measure() in remember con dipendenze di testo e stile. Non chiamare mai measure() all’interno di CompositionLocal o in percorsi di ricomposizione caldi.

Seconda regola — limita maxWidth e maxHeight. Se imposti maxWidth = Constraints.Infinity, TextMeasurer non potrà andare a capo e il testo uscirà dallo schermo. Passa sempre vincoli realistici, specialmente per il testo multilinea.

Terza regola — evita misurazioni ripetute con gli stessi parametri. Se il testo e lo stile non sono cambiati, il risultato di measure() sarà lo stesso. Combina la misurazione con Deferred o LaunchedEffect per scenari asincroni.

Domande Frequenti

In cosa si differenzia TextMeasurer da onSizeChanged in Compose?

TextMeasurer misura il testo prima del rendering, mentre onSizeChanged misura dopo. TextMeasurer è necessario quando la dimensione del testo influenza il layout padre; onSizeChanged è adatto per logica reattiva dopo la composizione.

Si può misurare SpannableString con TextMeasurer?

Sì, TextMeasurer.measure() accetta AnnotatedString, che include SpannableString con stili inline, link e colori. Tutti gli span vengono considerati durante la misurazione.

Perché TextMeasurer restituisce dimensioni in pixel anziché in dp?

TextMeasurer opera a livello di rendering Skia, dove tutte le dimensioni sono in pixel. Converti tramite LocalDensity.current: density.run { width.toDp() }.

Come misurare il testo prima del caricamento di un font?

TextMeasurer utilizza FontFamily.Default fino al caricamento di un font personalizzato. Per una misurazione accurata, attendi il caricamento del font tramite FontResource e usa async/await.

TextMeasurer influisce sulle prestazioni delle liste LazyColumn?

Misurare il testo in ogni elemento di LazyColumn può ridurre gli FPS su dispositivi deboli. Ottimizzazione: memorizza nella cache il risultato tramite remember e limita la dimensione del testo.

Riepilogo

  • TextMeasurer — Un’API Compose per misurare il testo prima del rendering, restituisce TextLayoutResult.
  • Il metodo measure() accetta testo, stile e Constraints in pixel.
  • L’istanza viene creata tramite rememberTextMeasurer() e riutilizzata.
  • Per testo su singola riga, il risultato viene utilizzato per larghezza adattiva del contenitore.
  • Per testo multilinea — lineCount, getLineWidth() e getBoundingBox() forniscono il quadro completo.
  • Nei Layout personalizzati, TextMeasurer consente di allineare elementi alla prima riga di testo.
  • Memorizza nella cache il risultato tramite remember ed evita di misurare in percorsi di ricomposizione caldi.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche