Draw styled text using a TextMeasurer.
DrawTextAnnotatedStringSample
@Composable
fun DrawTextAnnotatedStringSample() {
val textMeasurer = rememberTextMeasurer()
Canvas(Modifier.fillMaxSize()) {
drawText(
textMeasurer = textMeasurer,
text =
buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Start)) { append("Hello") }
withStyle(ParagraphStyle(textAlign = TextAlign.End)) { append("World") }
},
)
}
}
DrawTextDrawWithCacheSample
/**
* This sample demonstrates how to use [drawWithCache] modifier to improve performance when drawing
* text on DrawScope. We can use [drawWithCache] to calculate the text layout once in
* [CacheDrawScope] and then repeatedly use the same [TextLayoutResult] in the draw phase.
*
* This approach improves performance when the text itself does not change but its draw attributes
* do change over time, such as during a color animation.
*/
@Composable
fun DrawTextDrawWithCacheSample() {
// We can disable implicit caching since we will cache in DrawWithCache
val textMeasurer = rememberTextMeasurer(cacheSize = 0)
// Apply the current text style from theme, otherwise TextStyle.Default will be used.
val materialTextStyle = LocalTextStyle.current
// Animate color repeatedly
val infiniteTransition = rememberInfiniteTransition()
val color by
infiniteTransition.animateColor(
initialValue = Color.Red,
targetValue = Color.Blue,
animationSpec = infiniteRepeatable(tween(1000)),
)
Box(
Modifier.fillMaxSize().drawWithCache {
// Text layout will be measured just once until the size of the drawing area or
// materialTextStyle changes.
val textLayoutResult =
textMeasurer.measure(
text = "Hello, World!",
style = materialTextStyle,
constraints =
Constraints.fixed(
width = (size.width / 2).roundToInt(),
height = (size.height / 2).roundToInt(),
),
overflow = TextOverflow.Ellipsis,
)
// color changes will only invalidate draw phase
onDrawWithContent {
drawContent()
drawText(
textLayoutResult,
color = color,
topLeft =
Offset(
(size.width - textLayoutResult.size.width) / 2,
(size.height - textLayoutResult.size.height) / 2,
),
)
}
}
)
}
DrawTextMeasureInLayoutSample
/**
* This sample demonstrates how to use layout phase to improve performance when drawing text on
* DrawScope. We can use [layout] Modifier or [Layout] composable to calculate the text layout
* during layout phase and then cache the result in a Snapshot aware state to draw it during draw
* phase.
*/
@Composable
fun DrawTextMeasureInLayoutSample() {
val textMeasurer = rememberTextMeasurer()
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
Canvas(
Modifier.fillMaxSize().layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
// TextLayout can be done any time prior to its use in draw, including in a
// background thread.
// In this sample, text layout is measured in layout modifier. This way the layout
// call can be restarted when async font loading completes due to the fact that
// `.measure` call is executed in `.layout`.
textLayoutResult =
textMeasurer.measure(text = "Hello, World!", style = TextStyle(fontSize = 24.sp))
layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
}
) {
// This happens during draw phase.
textLayoutResult?.let { drawText(it) }
}
}
DrawTextSample
@Composable
fun DrawTextSample() {
val textMeasurer = rememberTextMeasurer()
Canvas(Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello, World!") }
}
DrawTextStyledSample
@Composable
fun DrawTextStyledSample() {
val textMeasurer = rememberTextMeasurer()
Canvas(Modifier.fillMaxSize()) {
drawText(
textMeasurer = textMeasurer,
text = "Hello, World!",
style =
TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textDecoration = TextDecoration.Underline,
),
)
}
}