Drawing Text on a Compose Canvas with drawText

Text is drawn on a canvas using DrawScope’s drawText() function and a TextMeasurer instance. The role of TextMeasurer is to calculate the size of the text drawing based on factors such as font family and size. We can obtain a TextMeasurer instance by making a call to the rememberTextMeasurer() function as follows:

val textMeasurer = rememberTextMeasurer()Code language: Kotlin (kotlin)

Having obtained a TextMeasurer instance, we can pass it to the drawText() function along with the text to be drawn:

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Sample Text")
}Code language: Kotlin (kotlin)

While the above example displays a plain text string, text drawing works best when used with annotated strings (a topic covered in this book’s Jetpack Compose Annotated Strings and Brush Styles chapter). Try out text drawing within the CanvasDemo project by making the following changes to the MainActivity.kt file:

.
.
@Composable
fun MainScreen() {
    DrawText()
}
.
.
@OptIn(ExperimentalTextApi::class)
@Composable
fun DrawText() {

    val colorList: List<color> = listOf(Color.Black,
        Color.Blue, Color.Yellow, Color.Red, Color.Green, Color.Magenta)

    val textMeasurer = rememberTextMeasurer()

    val annotatedText = buildAnnotatedString {
        withStyle(
            style = SpanStyle(
                fontSize = 60.sp,
                fontWeight = FontWeight.ExtraBold,
                brush = Brush.verticalGradient(colors = colorList)
            )
        ) {
            append("Text Drawing")
        }
    }

    Canvas(modifier = Modifier.fillMaxSize()) {
        drawText(textMeasurer, annotatedText)
    }
}Code language: Kotlin (kotlin)

The code we have added declares a list of colors, obtains a TextMeasurer and builds an annotated string that uses a large font size with extra bold font weight. A brush style is then used to apply a vertical gradient consisting of the color list. Next, the text measurer and annotated string are passed to the drawText() function of a Canvas scope resulting in the following output displayed in the preview panel:

Figure 39-19

An interesting benefit of using TextMeasurer is that it gives us access to the dimensions of the drawn text. This information is beneficial when you need to include a background matching the text size. The text size can be obtained by passing the annotated string to TextMeasurer’s measure() function. The measure() function will return a TextLayoutResult object from which we can extract size properties.

To see this in action, modify the DrawText function as follows so that the text is drawn on an appropriately sized horizontal gradient background:

@OptIn(ExperimentalTextApi::class)
@Composable
fun DrawText() {
.
.
    Canvas(modifier = Modifier.fillMaxSize()) {

        val dimensions = textMeasurer.measure(annotatedText)

        drawRect(
            brush = Brush.horizontalGradient(colors = colorList),
            size = dimensions.size.toSize()
        )
        drawText(textMeasurer, annotatedText)
    }
}Code language: Kotlin (kotlin)

After making the above changes, the text should appear in the preview panel as illustrated in Figure 39-20:

Figure 39-20

Summary

The Compose Canvas component provides a surface on which to draw graphics. The Canvas DrawScope includes a set of functions that allow us to perform drawing operations within the canvas area, including drawing lines, shapes, gradients, images, text, and paths. In this chapter, we have explored some of the more common drawing features provided by Canvas and the DrawScope functions.


Categories