TextMeasurer gives different results than the actual Text in Jetpack Compose

128 views Asked by At

I'm trying to measure the number of lines of a Text without rendering it. I thought I could use a TextMeasurer, however the number of "measured" lines is different than the number of lines I get if I render the actual Text.

This is the code I'm using:

Box () {
    val textMeasurer = rememberTextMeasurer()
    val measuredLayoutResult = textMeasurer.measure(
        largeText)
    println("Measured lines: ${measuredLayoutResult.lineCount}, " +
            "Measured height: ${measuredLayoutResult.size.height}, " +
            "Measured width: ${measuredLayoutResult.size.width}")
    Text(
        largeText,
        onTextLayout = { textLayoutResult ->
            println("Actual lines: ${textLayoutResult.lineCount}, " +
                    "Actual height: ${textLayoutResult.size.height}, " +
                    "Actual width: ${textLayoutResult.size.width}")

        },
    )
}

I would expect the numbers to be the same, instead I'm getting the following: Measured lines: 25, Measured height: 1132, Measured width: 30154 Actual lines: 235, Actual height: 2088, Actual width: 1080

Any ideas what I am doing wrong?

2

There are 2 answers

1
Thracian On BEST ANSWER

When you don't assign constraints param to measure function of TextMeasurer it uses default Constraints with maxWidth and maxHeight with Constraints.Infinity.

@Stable
fun measure(
    text: String,
    style: TextStyle = TextStyle.Default,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    constraints: Constraints = Constraints(),
    layoutDirection: LayoutDirection = this.defaultLayoutDirection,
    density: Density = this.defaultDensity,
    fontFamilyResolver: FontFamily.Resolver = this.defaultFontFamilyResolver,
    skipCache: Boolean = false
)

Use a BoxWithConstraints and pass a Constraints from it to limit total width that text can be measured with

@Preview
@Composable
private fun Test() {

    val largeText =
        "some large tatasda sdasdasdsa dasdasd asda sdasd asdasd asdasdasdasdasdasdasdasdasd"
    BoxWithConstraints {

        val textMeasurer = rememberTextMeasurer()
        val measuredLayoutResult = textMeasurer.measure(
            constraints = constraints,
            text = largeText
        )
        println(
            "Measured lines: ${measuredLayoutResult.lineCount}, " +
                    "Measured height: ${measuredLayoutResult.size.height}, " +
                    "Measured width: ${measuredLayoutResult.size.width}"
        )
        Text(
            largeText,
            onTextLayout = { textLayoutResult ->
                println(
                    "Actual lines: ${textLayoutResult.lineCount}, " +
                            "Actual height: ${textLayoutResult.size.height}, " +
                            "Actual width: ${textLayoutResult.size.width}"
                )

            },
        )
    }
}
0
BenjyTec On

Make sure that you provide all text styling arguments that you applied to the Text Composable to the measure function too (in case you truncated the code you posted).

If the code you posted is the exact code you have, then my guess is that the Box with the TextMeasurer exceeds the screen, while the Text for some reason doesn't. According to the Box documentation,

The Box will size itself to fit the content, subject to the incoming constraints.

So you could try to define the size of the Box to fit the screen and see if the issue persists:

Box (modifier = Modifier.fillMaxSize()) {
    val textMeasurer = rememberTextMeasurer()
    val measuredLayoutResult = textMeasurer.measure(largeText)
    //...
}