1. 介绍:
Jetpack Compose 1.3.0 之前没有 DrawScope.drawText
API 不能直接在 Jetpack Compose 画布/Canvas 上绘制文本,必须通过 Android 原生 Canvas
的 API nativeCanvas.drawText
来绘制文本。
从 Jetpack Compose 1.3.0 引入了新的 API DrawScope.drawText()
在 Canvas 上绘制文本。在本文中我们将探讨新的 DrawScope.drawText()
API。这里是新 API 的官方介绍:https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html
现在(androidx.compose.ui:ui-text:1.4.1
)此 API 仍处于实验状态,将来可能会发生变化。
如下是 Jetpack Compose 1.3.0 之前使用 nativeCanvas
怎样绘制文本:
@Composable
fun NativeDrawText() {
val paint = Paint().asFrameworkPaint().apply {
this.color = Color.White.toArgb()
this.textSize = 58f
}
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
onDraw = {
drawRect(color = Color.Black)
drawIntoCanvas {
it.nativeCanvas.drawText("Hello nativeCanvas,\n Hello itmob.cn", 30f, 100f, paint)
}
},
)
}
2. DrawScope.drawText()
drawText
函数有 4 个重载,两个重载的参数是 TextMeasure
,其他两个重载函数的参数的是 TextLayoutResult
,使用这两种方式测量文本来绘制它。
2.1 使用 TextMeasurer 绘制文本
TextMeasure
也是一个新的实验性 API 负责测量整个文本,以便可以绘制它。使用 rememberTextMeasurer()
可以创建 TextMeasurer
实例,并且可以设置带有样式的注释字符串作为参数,详情可以查看官方文档:https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextMeasurer
@ExperimentalTextApi
fun DrawScope.drawText(
textMeasurer: TextMeasurer,
text: AnnotatedString,
topLeft: Offset = Offset.Zero,
style: TextStyle = TextStyle.Default,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
placeholders: List<AnnotatedString.Range<Placeholder>> = emptyList(),
size: Size = Size.Unspecified,
blendMode: BlendMode = DrawScope.DefaultBlendMode
) {
...
}
@ExperimentalTextApi
fun DrawScope.drawText(
textMeasurer: TextMeasurer,
text: String,
topLeft: Offset = Offset.Zero,
style: TextStyle = TextStyle.Default,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
size: Size = Size.Unspecified,
blendMode: BlendMode = DrawScope.DefaultBlendMode
) {
...
}
上面是 DrawScope.drawText
源码中使用 TextMeasurer
参数的函数,主要区别是它们的 text 参数,一个是 AnnotatedString
一个是 String
。
关于 AnnotatedString 可以参见其他文章:Jetpack Compose 中拥有样式的文本,Jetpack Compose 中的文字,怎样处理 Jetpack Compose 中文本的长按选择和点击事件(如何在 Text 中添加超链接?。
-
如下是使用 TextMeasurer
的 drawText
绘制文本的例子(使用 AnnotatedString 参数):
@OptIn(ExperimentalTextApi::class)
@Composable
fun DrawTextWithTextMeasure() {
val textMeasure = rememberTextMeasurer()
val text = buildAnnotatedString {
withStyle(
style = SpanStyle(
color = Color.White,
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
),
) {
append("Hello, ")
}
withStyle(
style = SpanStyle(
color = Color.Cyan,
fontSize = 22.sp,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Bold,
),
) {
append("itmob.cn")
}
}
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
onDraw = {
drawRect(color = Color.Black)
drawText(
textMeasurer = textMeasure,
text = text,
topLeft = Offset(16.dp.toPx(), 16.dp.toPx()),
)
},
)
}
-
如下是使用 TextMeasurer
的 drawText
绘制文本的例子(使用 String 参数):
这个重载函数只支持一种文本样式,除了 text 参数是字符串类型而不是带注释的字符串,其他参数与上一个 drawText
重载函数相同。
@OptIn(ExperimentalTextApi::class)
@Composable
fun DrawTextWithTextMeasure() {
val textMeasure = rememberTextMeasurer()
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
onDraw = {
drawRect(color = Color.Black)
drawText(
textMeasurer = textMeasure,
text = "Hello, itmob.cn",
style = TextStyle(
color = Color.White,
fontSize = 22.sp,
),
topLeft = Offset(20.dp.toPx(), 20.dp.toPx()),
)
},
)
}
2.2 使用 TextLayoutResult 绘制文本
@ExperimentalTextApi
fun DrawScope.drawText(
textLayoutResult: TextLayoutResult,
color: Color = Color.Unspecified,
topLeft: Offset = Offset.Zero,
alpha: Float = Float.NaN,
shadow: Shadow? = null,
textDecoration: TextDecoration? = null,
drawStyle: DrawStyle? = null,
blendMode: BlendMode = DrawScope.DefaultBlendMode
) {
...
}
@ExperimentalTextApi
fun DrawScope.drawText(
textLayoutResult: TextLayoutResult,
brush: Brush,
topLeft: Offset = Offset.Zero,
alpha: Float = Float.NaN,
shadow: Shadow? = null,
textDecoration: TextDecoration? = null,
drawStyle: DrawStyle? = null,
blendMode: BlendMode = DrawScope.DefaultBlendMode
) {
...
}
上面是 DrawScope.drawText
源码中使用 TextLayoutResult
参数的函数,在画布上绘制现有的文本布局。主要区别是一个是使用颜色/Color 给文本上色,一个是使用笔刷/Brush 给文本上色。
下面的例子是使用创建的 TextLayoutResult
来绘制文本:
@OptIn(ExperimentalTextApi::class)
@Composable
fun DrawTextWithTextLayoutResult() {
val textMeasure = rememberTextMeasurer()
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
textLayoutResult = textMeasure.measure(
text = buildAnnotatedString {
withStyle(
style = SpanStyle(
color = Color.White,
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
),
) {
append("Hello, ")
}
withStyle(
style = SpanStyle(
color = Color.Cyan,
fontSize = 22.sp,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Bold,
),
) {
append("itmob.cn")
}
},
)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
},
) {
drawRect(color = Color.Black)
textLayoutResult?.let {
drawText(
textLayoutResult = it,
color = Color.White,
textDecoration = TextDecoration.Underline,
topLeft = Offset(16.dp.toPx(), 16.dp.toPx()),
)
}
}
}
通过 TextMeasure.measure()
创建 TextLayoutResult
。这里我们使用 LayoutModifier
中创建TextLayoutResult
。
另一个使用 Brush 作为参数为文字上色的重载函数这里不做过多介绍,跟上例类似。
drawText 的 color 参数只在 AnnotatedString 中未指定文字颜色时生效。
创建 TextLayoutResult
时只能使用 AnnotatedString 不能像使用 TextMeasurer
的重载函数那样简单的使用 String 类型字符串(至少写本文时 Jetpack Compose 1.4.1 仍然不可以)。
其他
更多关于 Jetpack Compose 文本的介绍:
在 Jetpack Compose 中怎样实现印章样式(弧形)的文字效果?
Jetpack Compose 中 DrawStyle 详解 - (线段/笔画/轮廓的绘制样式)
Jetpack Compose 中如何自定义文本笔画的描边效果
Jetpack Compose 中怎样隐藏或禁用 TextField(文本字段)的光标、文本选择手柄和文本工具栏?
怎样处理 Jetpack Compose 中文本的长按选择和点击事件(如何在 Text 中添加超链接?)
Jetpack Compose TextField VisualTransformation 视觉转换详解-高亮显示URL-格式化显示银行卡号码
怎样处理 Jetpack Compose 中文本的长按选择和点击事件(如何在 Text 中添加超链接?)
Jetpack Compose 怎样实现跑马灯效果(Marquee)
在 Jetpack Compose 中使用 DrawScope.drawText API 将文本绘制到 Canvas 上