1

I was trying to create custom text rendering composable using the the Canvas composable. However, I don't understand how TextMeasurer is calculating the final height of the composable.

Here is a minimal example of what I have tried till now.

@Composable
fun ShyText(
    text: String,
    modifier: Modifier = Modifier
) {
    val textMeasurer = rememberTextMeasurer()
    val measuredText =
        textMeasurer.measure(
            AnnotatedString(text),
            overflow = TextOverflow.Ellipsis,
            constraints = Constraints.fixedWidth(1080),
            style = TextStyle(
                fontSize = 18.sp,
            )
        )
    Canvas(
        Modifier
            .fillMaxWidth()
            .height(measuredText.size.height.dp) //I think the bug is coming from this line.
            .background(Color.DarkGray)
    ) {
        drawText(
            measuredText,
        )

    }
}

Here is how the composable is used.

ShyTextTheme {
      Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
          Column(Modifier.padding(innerPadding)) {
                ShyText(
                   text = sampletext,
                )
                Text("text")
                }
            }
}

This is the current result Screenshot of the text composable with a large blank space below it

I am trying to render without the blank space above "text" and the final line of the paragraph. I would like to know why the blank space is being rendered. (The grey background is just for testing)

2 Answers 2

1

That's not bug. What's returned from measuredText.size.height is in pixels. But you set height in dp directly from pixel value by adding dp end of it. density independent pixels are calculated with pixel value / density. If you get 200px from measured height on a device with 2.0 density it should be 100.dp, on a device with 4.0 density it should be 50.dp.

You just need to change it to

val density = LocalDensity.current
val heightDp = with(density){measuredText.size.height.toDp()}
Canvas(
    Modifier
        .fillMaxWidth()
        .height(heightDp) //I think the bug is coming from this line.
        .background(Color.DarkGray)
) {
    drawText(
        measuredText,
    )

}

This is how conversion is done with toDp()

/** Convert a [Float] pixel value to a Dp */
@Stable
fun Float.toDp(): Dp = (this / density).dp
Sign up to request clarification or add additional context in comments.

1 Comment

I had my suspicions that it had something to do with float/dp conversion. I tried your solution and it works great. Thanks for solving my issue.
0

I don't not if this might help, I'm currently learning android studio, language in use is java. If there is a view on the xml file you just drop the element you wish to place, e.g text, button e.t.c, after which you place your mouse on the borders of the element and move an arrow line toward the edge of the window(phone) for both sides and it will usually be centered. It doesn't have to be the edge it could also be an element.

1 Comment

I decided to make this composable as a challenge to learn more about Jetpack compose. If I had taken the traditional route of using Views, your answer might have been useful. But thanks anyway for the feedback.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.