随着团队的不断更新,项目组逐渐转到 Jetpack Compose 方式,对于 Compose 绘制 UI 方式在不断的熟悉,而开发过程中会遇到很多不同的场景,和尚在此记录一下常用的 Text 文本和 AnnotatedString 多种样式的文本的基本数据结构;
fun Text(
text: String,
modifier: Modifier = Modifier, // 修饰器
color: Color = Color.Unspecified, // 文本颜色
fontSize: TextUnit = TextUnit.Unspecified, // 文本字号
fontStyle: FontStyle? = null, // 文本字体样式
fontWeight: FontWeight? = null, // 文本粗细
fontFamily: FontFamily? = null, // 文本字体
letterSpacing: TextUnit = TextUnit.Unspecified, // 文本字间距
textDecoration: TextDecoration? = null, // 文本修饰器
textAlign: TextAlign? = null, // 文本对齐方式
lineHeight: TextUnit = TextUnit.Unspecified, // 文本行高
overflow: TextOverflow = TextOverflow.Clip, // 文本截取方式
softWrap: Boolean = true, // 文本是否换行
maxLines: Int = Int.MAX_VALUE, // 文本最大行数
onTextLayout: (TextLayoutResult) -> Unit = {},// 文本布局回调监听
style: TextStyle = LocalTextStyle.current // 文本样式
)
fun Text(
text: AnnotatedString,
inlineContent: Map<String, InlineTextContent> = mapOf(), // 替换范围内容的映射存储
...
)
Text 文本有两类构造方法,一类是传递 String 字符串类型的 text;另一类是传递 AnnotatedString 多种样式文本的 text;和尚首先会对 Text 基本属性进行学习整理;
color 用于设置文本颜色;fontSize 用于设置文本字号;
val name = "Compose Text 文本 & AnnotatedString 多种样式的文本的基本数据结构"
Text(
text = "$name, color = colorResource(id = R.color.teal_700)",
color = colorResource(id = R.color.teal_700)
)
Text(
text = "$name, fontSize = 18.sp",
color = colorResource(id = R.color.teal_700),
fontSize = 18.sp
)
fontWeight 用于设置文本粗细;fontFamily 用于设置文本字体,Android 系统默认四种字体,也可以按照需求下载自定义字体,注意引入时需要完整路径;
Text(
text = "$name, fontWeight = FontWeight.Light",
color = colorResource(id = R.color.teal_700),
fontWeight = FontWeight.Light
)
Text(
text = "$name, fontWeight = FontWeight.Bold",
color = colorResource(id = R.color.teal_700),
fontWeight = FontWeight.Bold
)
Text(
text = "$name, fontFamily = FontFamily.Cursive",
color = colorResource(id = R.color.teal_700),
fontFamily = FontFamily.Cursive
)
Text(
text = "$name, fontFamily = FontFamily.Serif",
color = colorResource(id = R.color.teal_700),
fontFamily = FontFamily.Serif
)
letterSpacing 用于设置文本字间距;textDecoration 用于设置文本修饰器,可以设置文本的删除线、下划线等;
Text(text = "$name, letterSpacing = 2.sp", letterSpacing = 2.sp)
Text(text = "$name, letterSpacing = 4.sp", letterSpacing = 4.sp)
Text(text = "$name, TextDecoration.None", textDecoration = TextDecoration.None)
Text(text = "$name, TextDecoration.LineThrough", textDecoration = TextDecoration.LineThrough)
Text(text = "$name, TextDecoration.Underline", textDecoration = TextDecoration.Underline)
textAlign 用于设置文本对齐方式;
Text(text = "$name, TextAlign.Left", textAlign = TextAlign.Left)
Text(text = "$name, TextAlign.Start", textAlign = TextAlign.Start)
Text(text = "$name, TextAlign.Right", textAlign = TextAlign.Right)
Text(text = "$name, TextAlign.End", textAlign = TextAlign.End)
Text(text = "$name, TextAlign.Center", textAlign = TextAlign.Center)
Text(text = "$name, TextAlign.Justify", textAlign = TextAlign.Justify)
lineHeight 用于设置文本行高;maxLines 用于设置文本显示的最大行数;
Text(text = "$name, lineHeight = 14.sp", lineHeight = 14.sp)
Text(text = "$name, lineHeight = 20.sp", lineHeight = 20.sp)
Text(text = "$name, lineHeight = 26.sp", lineHeight = 26.sp)
Text(text = "$name, maxLines = 1", maxLines = 1)
Text(text = "$name, maxLines = 3", maxLines = 3)
overflow 用于设置文本截取方式,通常配合 maxLines 共同使用;
Text(text = "$name, TextOverflow.Clip", overflow = TextOverflow.Clip, maxLines = 2)
Text(text = "$name, TextOverflow.Ellipsis", overflow = TextOverflow.Ellipsis, maxLines = 2)
Box(
modifier = Modifier.height(100.dp)
.background(color = colorResource(id = R.color.teal_700))
) {
Text(
text = "$name, overflow = TextOverflow.Visible",
modifier = Modifier
.height(50.dp).padding(5.dp)
.background(color = colorResource(id = R.color.white)),
overflow = TextOverflow.Visible
)
}
Box(
modifier = Modifier.height(100.dp)
.background(color = colorResource(id = R.color.purple_200))
) {
Text(
text = "$name, overflow = TextOverflow.Visible",
modifier = Modifier
.height(50.dp).padding(5.dp)
.background(color = colorResource(id = R.color.white))
)
}
softWrap 用于设置文本是否截取换行;设为 false 时不会进行换行,会认为屏幕宽度无限;true 时正常根据设置文本宽度换行;
Text(text = "$name, softWrap = true", softWrap = true)
Text(text = "$name, softWrap = false", softWrap = false)
modifier 属性存在于几乎所有的 Compose UI 组件中,是一个有序的、不可变的修饰符元素集合,用于装饰或添加 Compose UI 元素的行为,常用于设置组件的背景、尺寸、各类属性、点击事件等;和尚尝试几种常见的 Modifier 使用场景;
modifier 涉及到众多的 API,基本都离不开这三个重要的库;
Text(
text = "$name, Modifier.background(color, shape)",
modifier = Modifier.background(
color = colorResource(id = R.color.teal_700),
shape = RectangleShape
)
)
Text(
text = "$name, Modifier.background(brush, shape, alpha)",
modifier = Modifier.background(
brush = Brush.linearGradient(
colors = listOf(
colorResource(id = R.color.teal_200),
colorResource(id = R.color.teal_700),
colorResource(id = R.color.purple_200),
colorResource(id = R.color.purple_500)
)
),
shape = RectangleShape,
alpha = 0.8f
)
)
Modifier.border 边框设置有三个构造方法,通过 BorderStroke 设置边框或直接通过 width 等属性进行设置,大同小异,同样涉及 color 背景色和 brush 渐变色区分;
Text(
text = "$name, Modifier.background(border, shape)",
modifier = Modifier.border(
border = BorderStroke(
width = 1.dp,
color = colorResource(id = R.color.teal_700)
),
shape = RectangleShape
)
)
Text(
text = "$name, Modifier.background(width,color, shape)",
modifier = Modifier.border(
width = 1.dp,
color = colorResource(id = R.color.teal_700),
shape = RectangleShape
)
)
Text(
text = "$name, Modifier.border(width, brush, shape)",
modifier = Modifier.border(
width = 1.dp,
brush = Brush.linearGradient(
colors = listOf(
colorResource(id = R.color.teal_200),
colorResource(id = R.color.teal_700),
colorResource(id = R.color.purple_200),
colorResource(id = R.color.purple_500)
)
),
shape = RectangleShape
)
)
Modifier.shadow 阴影主要设置 elevation 阴影高度,其中 clip 主要用于是否进行内容裁剪;
Text(
text = "$name, Modifier.shadow(elevation, shape, clip)",
modifier = Modifier.shadow(
elevation = 10.dp,
shape = RoundedCornerShape(50),
clip = true
)
)
Text(
text = "$name, Modifier.shadow(elevation, shape, clip)",
modifier = Modifier.shadow(
elevation = 10.dp,
shape = RoundedCornerShape(50),
clip = false
)
)
Modifier.clip 可以用于不同样式的裁切小古偶,但需要注意的是,裁切的是文本内容,而不会裁切对应的 background 等;
Text(
text = "$name, Modifier.clip(shape)",
modifier = Modifier
.background(color = colorResource(id = R.color.teal_700), shape = RectangleShape
.clip(shape = RoundedCornerShape(50))
)
Modifier.scale 有两类构造方法;
Text(
text = "$name, Modifier.scale(2.0f)",
modifier = Modifier.scale(2.0f)
)
Text(
text = "$name, Modifier.scale(scaleX = 2.0f, scaleY = 1.0f)",
modifier = Modifier.scale(scaleX = 2.0f, scaleY = 1.0f)
)
Modifier.rotate 用于元素旋转,旋转角度以元素中心为原点,按照顺时针方向进行角度旋转;
Text(text = "$name, Modifier.rotate(45f)", modifier = Modifier.rotate(45f))
Modifier.clickable 用于单击事件;Modifier.combinedClickable 用于多种点击事件的联合构造方法;
Text(
text = "$name, Modifier.clickable()",
modifier = Modifier.clickable(
enabled = true,
onClick = {
Toast.makeText(context, "Modifier.clickable()", Toast.LENGTH_LONG).show()
}
)
)
fontStyle 用于设置文本字体样式,包括 Normal 和 Italic 斜体两类;style 用于设置文本内容样式,style 方法中的多种属性与 Text 属性重叠,当两者均设置时,以 Text 属性为准;
Text(text = "$name, FontStyle.Normal", fontStyle = FontStyle.Normal)
Text(text = "$name, FontStyle.Italic", fontStyle = FontStyle.Italic)
TextStyle 的 background 用于设置文字背景色;brush 用于设置文字渐变色;
Text(
text = "$name, TextStyle(background)",
modifier = Modifier
.background(
color = colorResource(id = R.color.teal_700),
shape = RectangleShape,
),
style = TextStyle(background = colorResource(id = R.color.purple_200))
)
Text(
text = "$name, TextStyle(brush)",
modifier = Modifier
.background(
color = colorResource(id = R.color.teal_700),
shape = RectangleShape,
),
style = TextStyle(
brush = Brush.linearGradient(
colors = listOf(
colorResource(id = R.color.white),
colorResource(id = R.color.purple_200),
colorResource(id = R.color.purple_500)
)
), alpha = 0.8f
)
)
TextStyle 的 baselineShift 用来让所有文字互相对齐的基准线,视觉上更舒适; fontSynthesis 用于合成字体,当使用的 FontFamily 不包含粗体或斜体时,系统是否应该伪造粗体或斜体;
TextStyle 的 textIndent 用于设置文本缩进,设置缩进位置;shadow 用于设置文本阴影,仅限于文字内容,其中 blurRadius 用于设置阴影模糊半径;
Text(
text = "$name, TextStyle(textIndent)",
style = TextStyle(textIndent = TextIndent(firstLine = 30.sp))
)
Text(
text = "$name, TextStyle(shadow)",
style = TextStyle(
shadow = Shadow(
color = colorResource(id = R.color.teal_700),
offset = Offset.Zero,
blurRadius = 10f
)
)
)
textGeometricTransform 用于设置文本几何变换,可以设置文本的 scaleX 水平缩放和 skewX 的 X 轴斜度;fontFeatureSettings 属于高级排版,用 CSS 的 font-feature-settings 的方式来设置文字样式;
Text(
text = "$name, TextStyle(textGeometricTransform)",
style = TextStyle(textGeometricTransform = TextGeometricTransform(scaleX = 2.0f, skewX = 0.5f))
)
Text(
text = "$name, TextStyle(fontFeatureSettings)",
style = TextStyle(fontFeatureSettings = "smcp")
)
onTextLayout 用于布局回调监听,通过监听可以更合理、更灵活的方式排布和显示文本,允许在文本布局完成后执行自定义操作;但需要注意的是:onTextLayout 回调函数将在每次文本布局更改时被调用,因此请确保避免在该函数中执行耗时的操作,以确保性能;
和尚通过 TextLayoutResult.layoutInput 获取生成该文本布局结果的输入参数,如文本内容、文本样式、布局约束等,可以根据这些信息执行其他操作,比如根据不同的文本输入参数采取不同的处理逻辑;
Text(
text = "$name, TextStyle(layoutResult)",
onTextLayout = { layoutResult: TextLayoutResult ->
val layoutInput = layoutResult.layoutInput
val text = layoutInput.text // 获取文本内容
val style = layoutInput.style // 获取文本样式
val constraints = layoutInput.constraints // 获取布局约束
Log.e("layoutInput.text:", "$text")
Log.e("layoutInput.style:", "$style")
Log.e("layoutInput.constraints:", "$constraints")
}
)
在 Java / Kotlin 中使用 TextView 设置富文本样式时,例如文本段落中添加链接、设置部分内容特定颜色、尺寸等;传递的是 SpannableString,而 Compose 中使用 Text 设置富文本样式时,传递的是 AnnotatedString;
其中 Spanned 有四种类型:
val name = "Kotlin TextView 通过 SpannableString 设置富文本效果!"
val spannableName = SpannableString(name)
val blueSpan = ForegroundColorSpan(Color.BLUE)
val redSpan = ForegroundColorSpan(Color.RED)
val boldStyle = StyleSpan(Typeface.BOLD)
val italicStyle = StyleSpan(Typeface.ITALIC)
spannableName.setSpan(blueSpan, 0, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableName.setSpan(boldStyle, 0, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableName.setSpan(redSpan, 18, 34, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableName.setSpan(italicStyle, 18, 34, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
viewBinding.textTv.text = spannableName
AnnotatedString 是在 Compose 中用于处理富文本和样式的字符串类,可以通过使用不同的修饰符和标记来对这些文本片段进行标记,并为每个标记应用特定的样式;AnnotatedString 通常使用 buildAnnotatedString 方式进行创建对应的 AnnotatedString 对象;
val annotatedString1 = buildAnnotatedString {
append("Compose Text ")
append("通过 AnnotatedString 设置富文本效果!")
}
Text(text = annotatedString1)
val annotatedString2 = buildAnnotatedString {
withStyle(style = ParagraphStyle(textIndent = TextIndent(20.sp))) {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue)) {
append("Compose Text ")
}
}
withStyle(style = SpanStyle()) {
append("通过")
}
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic, color = Color.Red)) {
append(" AnnotatedString ")
}
withStyle(style = SpanStyle()) {
append("设置富文本效果!")
}
withStyle(style = ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue)) {
append("Welcome to AnnotatedString!")
}
}
}
Text(text = annotatedString2)
val annotatedString3 = buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue)) {
append("Compose Text ")
}
withStyle(style = SpanStyle()) {
append("通过")
}
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic, color = Color.Red)) {
append(" AnnotatedString ")
}
withStyle(style = SpanStyle()) {
append("设置富文本效果!")
}
withStyle(style = SpanStyle(color = Color.Red)) {
append("点击跳转 baidu.com!")
}
addStringAnnotation(
tag = "URL",
annotation = "https://www.baidu.com",
start = 40,
end = 55
)
}
Text(text = annotatedString3, modifier = Modifier.clickable {
annotatedString3.getStringAnnotations("URL", 7, 14)
.firstOrNull()?.let { annotation ->
// annotation.item 为链接的标记数据
Toast.makeText(context, "跳转 baidu.com", Toast.LENGTH_LONG).show()
}
})
ClickableText(text = annotatedString3, onClick = { integer ->
annotatedString3.getStringAnnotations("URL", integer, integer).firstOrNull()?.let {
Toast.makeText(context, "跳转 baidu.com", Toast.LENGTH_LONG).show()
}
})
inlineContent 用于替换范围内容的映射存储;可以在 Text 中添加占位等操作;用于在文本中添加内联内容,并提供自定义的渲染逻辑;内联内容可以是特殊标记或占位符,用于在文本中插入自定义的组件或视图;
val annotatedString4 = buildAnnotatedString {
appendInlineContent(id = "inline1")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue)) {
append("Compose Text ")
}
withStyle(style = SpanStyle()) {
append("通过")
}
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic, color = Color.Red)) {
append(" AnnotatedString ")
}
withStyle(style = SpanStyle()) {
append("设置富文本效果!")
}
}
Text(
text = annotatedString4, inlineContent = mapOf("inline1" to InlineTextContent(
Placeholder(20.sp, 10.sp, PlaceholderVerticalAlign.TextCenter)
) {
Box(
modifier = Modifier
.width(10.dp)
.height(10.dp)
.background(
color = colorResource(id = R.color.teal_700),
shape = RoundedCornerShape(5.dp)
)
)
})
)
Text(
text = annotatedString4, inlineContent = mapOf("inline1" to InlineTextContent(
Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.Bottom)
) {
Image(
painter = painterResource(id = R.mipmap.ic_icon),
modifier = Modifier.width(20.dp).height(20.dp),
contentDescription = ""
)
})
)
和尚对 Text 和 AnnotatedString 的应理解和使用仅限于日常应用,对底层探究较少,如有错误,请多多指导!
阿策小和尚
Compose UI 发展至今,随着高性能和易用性,逐渐替换了历史的 Java / Kotlin 模式开发,而突然转向 Compose 开发,还是会有一段适应期,而 Text 作为最常用的组件,使用方式和场景有了更多的方式。和尚以此为出发点,事无巨细,详细介绍了 Text 的各类属性和效果图,同时对富文本的 AnnotatedString 进行了案例分析,帮助更多开发者更快的了解和应用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。