4

I am creating a custom composable in Jetpack Compose using Canvas.
How to center text when using drawText?

Code

@OptIn(ExperimentalTextApi::class)
@Composable
fun MyCenterTextInCanvas() {
    val width: Dp = 200.dp
    val height: Dp = 40.dp
    val textMeasurer = rememberTextMeasurer()
    Canvas(
        modifier = Modifier
            .background(Color.LightGray)
            .wrapContentSize(
                align = Alignment.Center,
            )
            .requiredSize(
                width = width,
                height = height,
            ),
    ) {
        drawText(
            textMeasurer = textMeasurer,
            text = "Sample Text",
            topLeft = Offset(
                x = (width / 2).toPx(),
                y = (height / 2).toPx(),
            ),
        )
    }
}

Compose version
jetpackComposeVersion = "1.3.0-alpha02"

UI

enter image description here

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121

1 Answers1

10

You can do it by measuring text and placing it as

@OptIn(ExperimentalTextApi::class)
@Composable
fun MyCenterTextInCanvas() {
    val width: Dp = 200.dp
    val height: Dp = 40.dp
    val textMeasurer = rememberTextMeasurer()

    val textLayoutResult: TextLayoutResult =
        textMeasurer.measure(text = AnnotatedString("Sample Text"))
    val textSize = textLayoutResult.size
    Canvas(
        modifier = Modifier
            .background(Color.LightGray)
            .requiredSize(
                width = width,
                height = height,
            ),
    ) {

        val canvasWidth = size.width
        val canvasHeight = size.height


        drawText(
            textMeasurer = textMeasurer,
            text = "Sample Text",
            topLeft = Offset(
                (canvasWidth - textSize.width) / 2f,
                (canvasHeight - textSize.height) / 2f
            ),
        )
    }
}

enter image description here

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • 2
    Thank You. . I did not know how to use Text Measurer. – Abhimanyu Aug 14 '22 at 15:59
  • 2
    Although the centerling logic is correct, please do not measure text in composition. Ideally text measuring should be a part of the layout phase. Also, measured text should be remembered/cached. You can use Modifier.drawWithCache on a Box instead of Canvas composable to cache a `TextLayoutResult`. If you check the source code of Canvas, you'll see there's nothing special to it. In any case, TextMeasurer caches results internally by default. If attributes do not change, text layout will be skipped and result will be returned from the internal cache. – Halil Ibrahim Ozercan Sep 06 '22 at 13:42
  • 2
    @HalilIbrahimOzercan sure, canvas is nothing but Spacer with Modifier.drawContent but how can we move text measurement to layout phase? In this answer i moved measurement of text to remember. https://stackoverflow.com/a/73547525/5457853. Is this correct? Also would you mind posting examples with layout phase and `Modifier.drawWithCache`? – Thracian Sep 06 '22 at 13:59