TextMeasurer: mi ez és hogyan működik a szövegmérés a Compose-ban

Szerző: IT Sectr Megjelenés: 2026-07-25 Olvasási idő: 8 perc

TextMeasurer — egy API a Jetpack Compose-ból, amely a szöveg méreteinek mérésére szolgál a képernyőn történő tényleges megjelenítés előtt. Ellentétben a klasszikus megközelítéssel, amely a Paint.measureText-et használja Android View-ban, a TextMeasurer egy Compose-natív módszert biztosít a sorok szélességének, magasságának és metrikáinak megszerzésére, figyelembe véve a stílust és a korlátozásokat. Az Android Developers Documentation (2025) szerint a TextMeasurer aktívan használatos egyedi Layout komponensekben, animált feliratokban és adaptív UI elemekben, ahol a szöveg mérete befolyásolja más elemek pozicionálását.

Főbb pontok

  • TextMeasurer — Compose API a szöveg renderelés előtti mérésére.
  • A measure() metódus TextLayoutResult-ot ad vissza sor- és karaktermetrikákkal.
  • Az API figyelembe veszi a stílust, betűtípust, méretet és a konténer korlátozásait pixelben.
  • A TextMeasurer támogatja egy sor és a többsoros szöveg mérését.
  • A mérés eredménye egyedi elrendezéshez és szöveganimációhoz használható.

Mi az a TextMeasurer a Jetpack Compose-ban?

TextMeasurer — egy funkcionális interfész az androidx.compose.ui.text csomagból, amely lehetővé teszi a szöveg mérését a Compose-ban anélkül, hogy közvetlenül megjelenítené a képernyőn. Része a Compose szövegmotorjának, és a Paragraph API szintjén működik, amely a Skia-t használja a rendereléshez.

A TextMeasurer fő célja az előzetes mérés. A Compose-ban a szöveg mérete általában azután kerül meghatározásra, hogy a Composable már elhelyezésre került a Layout-ban. De vannak olyan forgatókönyvek, ahol a szöveg szélességét a kompozíció előtt kell tudni: például annak eldöntéséhez, hogy egy sor befér-e a konténerbe, vagy a gomb méretének kiszámításához a szöveg hossza alapján.

A TextMeasurer ezt a problémát a measure() metódus biztosításával oldja meg, amely fogadja a TextMeasureRequest-ot (szöveg, stílus, korlátozások) és visszaadja a TextLayoutResult-ot (metrikák, sorok, karakterek). Ez lehetővé teszi a fejlesztő számára, hogy a szövegről minden adatot szinkron módon, egy képkocka késleltetés nélkül megszerezzen.

Használja a TextMeasurer-t, amikor a szöveg mérete befolyásolja a szülőkomponens méretét, animált szövegátmenetek létrehozásakor, vagy egyedi Layout-ok implementálásakor, ahol a szöveg más elemekkel szomszédos.

Hogyan működik a TextMeasurer

A TextMeasurer API két kulcsfontosságú entitásból áll: rememberTextMeasurer() (példány létrehozása) és measure() (mérés elindítása). A példány egyszer jön létre a komponens teljes életciklusára, és újra felhasználható az összes méréshez.

kotlin
val textMeasurer = rememberTextMeasurer()

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

// eredmény szélessége, eredmény magassága, sorok száma

A constraints paraméter határozza meg a maximális szélességet és magasságot, amelyen belül a szövegnek el kell férnie. Ha a szöveg nem fér el szélességben, a következő sorba kerül. A mérési eredmény tartalmazza a TextLayoutResult-ot, amelyben lekérhető a sorok száma, az egyes karakterek pozíciója, az egyes sorok szélessége és a szöveg teljes mérete.

A TextMeasurer szinkron módon működik az UI szálban, és nem okoz újrakomponálást. Azonban a szöveg gyakori mérése különböző stílusokkal terhelést okozhat — ezért a példány a remember-en keresztül jön létre és használható újra.

Egysoros szöveg mérése

A legegyszerűbb forgatókönyv — mérje meg az egysoros szöveg szélességét a szülő konténer méretének meghatározásához. Például egy gomb esetében, amelynek szélessége a felirat hosszától függ.

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

Ebben a példában a remember(label) garantálja, hogy a mérés csak a szöveg változásakor indul újra. A Constraints nagy maxWidth (1000) értékkel van beállítva a sortörés megelőzésére — a tényleges korlátozás megegyezik a szöveg szélességével. A Box szélessége a mért szövegszélességre van állítva, ami a „gomb a szöveg méretéhez igazítva" effektust adja.

Figyelem: az eredményből származó szélességet .dp-n keresztül dp-vé kell konvertálni, mivel a TextMeasurer pixelekben adja vissza a méreteket, míg a Modifier.width Dp-t vár. A pontosság érdekében használja a density-t és a LocalDensity.current-et.

Többsoros szöveg mérése

Többsoros szöveg esetén a TextMeasurer lehetővé teszi nemcsak a teljes magasság, hanem a sorok számának és az egyes karakterek pozíciójának meghatározását is a bekezdésen belül. Ez szükséges egyedi szövegmezők, csevegések vagy szerkesztők létrehozásakor.

A TextLayoutResult fő tulajdonságai többsoros szöveghez: lineCount (sorok száma), getLineTop(index) (a sor kezdetének Y-koordinátája), getLineBottom(index) (a sor végének Y-koordinátája), getLineWidth(index) (a sor szélessége). Ezek kombinálásával pontosan pozícionálhatja a felület elemeit a szövegsorokhoz viszonyítva.

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

A getLineWidth(lineIndex) metódus a megadott sor szélességét adja vissza pixelben. Kisebb lehet, mint a maxWidth, ha a sor nem éri el a konténer határát. Az egyes sorok szélességének ismeretében manuálisan implementálhatja a sorkizárt effektust, vagy inline elemeket helyezhet el a rövid sorok mellé.

Egy adott karakter pozíciójának lekéréséhez használja a getBoundingBox(offset)-ot, amely egy Rect-et ad vissza left, top, right, bottom koordinátákkal. Ez hasznos a kurzor pozicionálásához egyedi szövegmezőkben, vagy egy szövegtartomány animációval történő kiemeléséhez.

TextMeasurer egyedi komponensekben

A TextMeasurer legerőteljesebb alkalmazási forgatókönyve — egyedi Layout komponensek, ahol a szöveg más elemekkel szomszédos, és méreteik kölcsönösen függnek egymástól. Például az „Ikon + szöveg" widget, ahol az ikonnak a szöveg első sorához képest kell középre igazodnia, nem a teljes blokkhoz.

A szabványos Row-ban az ikon a teljes Row magasságához képest középre igazodik, ami természetellenesnek tűnik, amikor a szöveg 3 sort foglal el, az ikon pedig egyet. A TextMeasurer-rel megmérheti az első sort, és pontosan ahhoz igazíthatja az ikont.

kotlin
val textMeasurer = rememberTextMeasurer()

Layout(
    content = {
        Icon(imageVector = Icons.Default.Star, contentDescription = null)
        Text(text = "Többsoros szöveg itt")
    },
    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)
        }
    }
)

Ebben a példában az első sor TextMeasurer-en keresztüli mérése határozza meg az ikon pozícióját. TextMeasurer nélkül az ikon a többsoros szöveg teljes magasságához képest középre igazodna, ami vizuálisan helytelen. Ezt a mintát széles körben használják csevegésekben, névjegyzékekben és információ kártyákon.

Teljesítmény tippek

A TextMeasurer — egy szinkron API, amely az UI szálban működik. Túlzott használat esetén képkocka kiesést okozhat, különösen nagy mennyiségű szöveg mérésekor gyengébb eszközökön.

Első szabály — gyorsítótárazza az eredményt. Mindig csomagolja a measure() hívást remember-be a szövegtől és stílustól függően. Soha ne hívja a measure()-t CompositionLocal-on belül vagy a forró újrakomponálási útvonalakon.

Második szabály — korlátozza a maxWidth és maxHeight értékeket. Ha a maxWidth = Constraints.Infinity értéket állítja be, a TextMeasurer nem tud sortörést végezni, és a szöveg kilóg a képernyőről. Mindig adjon meg reális korlátozásokat, különösen többsoros szöveg esetén.

Harmadik — kerülje a mérés megismétlését ugyanazokkal a paraméterekkel. Ha a szöveg és a stílus nem változott, a measure() eredménye ugyanaz lesz. Kombinálja a mérést Deferred vagy LaunchedEffect segítségével aszinkron forgatókönyvekhez.

Gyakran ismételt kérdések

Miben különbözik a TextMeasurer az onSizeChanged-től a Compose-ban?

A TextMeasurer a renderelés előtt méri a szöveget, míg az onSizeChanged utána. A TextMeasurer akkor szükséges, amikor a szöveg mérete befolyásolja a szülő layout-ját; az onSizeChanged a kompozíció utáni reaktív logikához alkalmas.

Mérhető-e a SpannableString a TextMeasurer-en keresztül?

Igen, a TextMeasurer.measure() fogadja az AnnotatedString-et, amely inline stílusokkal, hivatkozásokkal és színekkel ellátott SpannableString-et tartalmaz. Az összes span figyelembevételre kerül a mérés során.

Miért pixelben adja vissza a TextMeasurer a méretet, nem dp-ben?

A TextMeasurer a Skia-renderelés szintjén működik, ahol minden méret pixelben van. Konvertálja a LocalDensity.current-en keresztül: density.run { width.toDp() }.

Hogyan mérjünk szöveget a betűtípus betöltése előtt?

A TextMeasurer FontFamily.Default-ot használ az egyedi betűtípus betöltéséig. A pontos méréshez várja meg a betöltést a FontResource-on keresztül, és használja az async/awaitet.

Befolyásolja-e a TextMeasurer a LazyColumn listák teljesítményét?

A szöveg mérése a LazyColumn minden elemében csökkentheti az FPS-t gyengébb eszközökön. Optimalizálás: gyorsítótárazza az eredményt a remember-en keresztül, és korlátozza a szöveg méretét.

Összefoglalás

  • TextMeasurer — Compose API a szöveg renderelés előtti mérésére, TextLayoutResult-ot ad vissza.
  • A measure() metódus fogadja a szöveget, stílust és Constraints-t pixelben.
  • A példány a rememberTextMeasurer()-en keresztül jön létre és újra használható.
  • Egysoros szöveg esetén az eredmény a konténer adaptív szélességéhez használható.
  • Többsoros szöveg esetén — a lineCount, a getLineWidth() és a getBoundingBox() teljes képet ad.
  • Egyedi Layout-okban a TextMeasurer lehetővé teszi az elemek igazítását a szöveg első sorához.
  • Gyorsítótárazza az eredményt a remember-en keresztül, és kerülje a mérést a forró újrakomponálási útvonalakon.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is