androidkotlinandroid-jetpack-composeandroid-jetpacktext-rendering

Blank space when creating a custom text rendering composable


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)


Solution

  • 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