前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Compose Text 文本和 AnnotatedString 多种样式的文本详解|技术创作特训营第一期

Compose Text 文本和 AnnotatedString 多种样式的文本详解|技术创作特训营第一期

原创
作者头像
阿策小和尚
发布2023-08-23 22:24:31
1.4K0
发布2023-08-23 22:24:31
举报
文章被收录于专栏:阿策小和尚阿策小和尚

    随着团队的不断更新,项目组逐渐转到 Jetpack Compose 方式,对于 Compose 绘制 UI 方式在不断的熟悉,而开发过程中会遇到很多不同的场景,和尚在此记录一下常用的 Text 文本和 AnnotatedString 多种样式的文本的基本数据结构;

Text

源码分析

代码语言:txt
复制
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 基本属性进行学习整理;

案例分析

1. color & fontSize

    color 用于设置文本颜色;fontSize 用于设置文本字号;

代码语言:txt
复制
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
)
2. fontWeight & fontFamily

    fontWeight 用于设置文本粗细;fontFamily 用于设置文本字体,Android 系统默认四种字体,也可以按照需求下载自定义字体,注意引入时需要完整路径;

代码语言:txt
复制
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
)
3. letterSpacing & textDecoration

    letterSpacing 用于设置文本字间距;textDecoration 用于设置文本修饰器,可以设置文本的删除线、下划线等;

代码语言:txt
复制
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)
4. textAlign

    textAlign 用于设置文本对齐方式;

  • TextAlign.Left 左对齐
  • TextAlign.Start 起始方向对齐;(例如:大部分语言是从左到右书写,而阿拉伯语言等是从右向左书写)
  • TextAlign.Right 右对齐
  • TextAlign.End 终点到起点方向对齐
  • TextAlign.Center 居中对齐
  • TextAlign.Justify 两端对齐
代码语言:txt
复制
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)
5. lineHeight & maxLines

    lineHeight 用于设置文本行高;maxLines 用于设置文本显示的最大行数;

代码语言:txt
复制
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)
6. overflow

    overflow 用于设置文本截取方式,通常配合 maxLines 共同使用;

  • TextOverflow.Clip 直接截取
  • TextOverflow.Ellipsis 小数点结尾
  • TextOverflow.Visible 较为特殊,若指定范围内没有足够的空间,Visible 时即便溢出也要展示;
代码语言:txt
复制
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))
    )
}
7. softWrap

    softWrap 用于设置文本是否截取换行;设为 false 时不会进行换行,会认为屏幕宽度无限;true 时正常根据设置文本宽度换行;

代码语言:txt
复制
Text(text = "$name, softWrap = true", softWrap = true)
Text(text = "$name, softWrap = false", softWrap = false)
8. modifier

    modifier 属性存在于几乎所有的 Compose UI 组件中,是一个有序的、不可变的修饰符元素集合,用于装饰或添加 Compose UI 元素的行为,常用于设置组件的背景、尺寸、各类属性、点击事件等;和尚尝试几种常见的 Modifier 使用场景;

    modifier 涉及到众多的 API,基本都离不开这三个重要的库;

  • androidx.compose.foundation.layout 与布局相关的库;
  • androidx.compose.ui.draw 与绘制相关的库;
  • androidx.compose.foundation 基础属性库,包括设置背景、尺寸、各类属性和点击事件;8.1 Modifier.background 背景    Modifier.background 背景设置有两个构造函数,分别对应设置 color 背景色和 brush 渐变色;与 Flutter 等大部分语言类似,colorbrush 不能同时使用;
代码语言:txt
复制
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
    )
)
8.2 Modifier.border 边框

    Modifier.border 边框设置有三个构造方法,通过 BorderStroke 设置边框或直接通过 width 等属性进行设置,大同小异,同样涉及 color 背景色和 brush 渐变色区分;

代码语言:txt
复制
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
    )
)
8.3 Modifier.shadow 阴影

    Modifier.shadow 阴影主要设置 elevation 阴影高度,其中 clip 主要用于是否进行内容裁剪;

代码语言:txt
复制
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
    )
)
8.4 Modifier.clip 裁切

    Modifier.clip 可以用于不同样式的裁切小古偶,但需要注意的是,裁切的是文本内容,而不会裁切对应的 background 等;

代码语言:txt
复制
Text(
    text = "$name, Modifier.clip(shape)",
    modifier = Modifier
        .background(color = colorResource(id = R.color.teal_700), shape = RectangleShape
        .clip(shape = RoundedCornerShape(50))
)
8.5 Modifier.scale 缩放

    Modifier.scale 有两类构造方法;

  • Modifier.scale 以元素中心等比例缩放;
  • Modifier.scale(scaleX, scaleY) 以元素中心按 X / Y 轴分别缩放;
代码语言:txt
复制
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)
)
8.6 Modifier.rotate 旋转

    Modifier.rotate 用于元素旋转,旋转角度以元素中心为原点,按照顺时针方向进行角度旋转;

代码语言:txt
复制
Text(text = "$name, Modifier.rotate(45f)", modifier = Modifier.rotate(45f))
8.7 Modifier.clickable & Modifier.combinedClickable 点击事件

    Modifier.clickable 用于单击事件;Modifier.combinedClickable 用于多种点击事件的联合构造方法;

代码语言:txt
复制
Text(
    text = "$name, Modifier.clickable()",
    modifier = Modifier.clickable(
        enabled = true,
        onClick = {
            Toast.makeText(context, "Modifier.clickable()", Toast.LENGTH_LONG).show()
        }
    )
)
9. fontStyle & style

    fontStyle 用于设置文本字体样式,包括 NormalItalic 斜体两类;style 用于设置文本内容样式,style 方法中的多种属性与 Text 属性重叠,当两者均设置时,以 Text 属性为准;

代码语言:txt
复制
Text(text = "$name, FontStyle.Normal", fontStyle = FontStyle.Normal)
Text(text = "$name, FontStyle.Italic", fontStyle = FontStyle.Italic)
9.1 background & brush

    TextStylebackground 用于设置文字背景色;brush 用于设置文字渐变色;

代码语言:txt
复制
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
    )
)
9.2 baselineShift & fontSynthesis

    TextStylebaselineShift 用来让所有文字互相对齐的基准线,视觉上更舒适; fontSynthesis 用于合成字体,当使用的 FontFamily 不包含粗体或斜体时,系统是否应该伪造粗体或斜体;

9.3 textIndent & shadow

    TextStyletextIndent 用于设置文本缩进,设置缩进位置;shadow 用于设置文本阴影,仅限于文字内容,其中 blurRadius 用于设置阴影模糊半径;

代码语言:txt
复制
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
        )
    )
)
9.4 textGeometricTransform & fontFeatureSettings

    textGeometricTransform 用于设置文本几何变换,可以设置文本的 scaleX 水平缩放和 skewXX 轴斜度;fontFeatureSettings 属于高级排版,用 CSSfont-feature-settings 的方式来设置文字样式;

代码语言:txt
复制
Text(
    text = "$name, TextStyle(textGeometricTransform)",
    style = TextStyle(textGeometricTransform = TextGeometricTransform(scaleX = 2.0f, skewX = 0.5f))
)
Text(
    text = "$name, TextStyle(fontFeatureSettings)",
    style = TextStyle(fontFeatureSettings = "smcp")
)
10. onTextLayout

    onTextLayout 用于布局回调监听,通过监听可以更合理、更灵活的方式排布和显示文本,允许在文本布局完成后执行自定义操作;但需要注意的是:onTextLayout 回调函数将在每次文本布局更改时被调用,因此请确保避免在该函数中执行耗时的操作,以确保性能;

    和尚通过 TextLayoutResult.layoutInput 获取生成该文本布局结果的输入参数,如文本内容、文本样式、布局约束等,可以根据这些信息执行其他操作,比如根据不同的文本输入参数采取不同的处理逻辑;

代码语言:txt
复制
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")
    }
)
11. SpannableString & AnnotatedString
11.1 SpannableString

    在 Java / Kotlin 中使用 TextView 设置富文本样式时,例如文本段落中添加链接、设置部分内容特定颜色、尺寸等;传递的是 SpannableString,而 Compose 中使用 Text 设置富文本样式时,传递的是 AnnotatedString

    其中 Spanned 有四种类型:

  • Spanned.SPAN_INCLUSIVE_EXCLUSIVE 从起始到终止下标设置,仅包括起始下标;
  • Spanned.SPAN_INCLUSIVE_INCLUSIVE 从起始到终止下标设置,同时包括起始和终止下标;
  • Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 从起始到终止下标设置,均不包括起始和终止下标;
  • Spanned.SPAN_EXCLUSIVE_INCLUSIVE 从起始到终止下标设置,仅包括终止下标;
代码语言:txt
复制
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
11.2 AnnotatedString

    AnnotatedString 是在 Compose 中用于处理富文本和样式的字符串类,可以通过使用不同的修饰符和标记来对这些文本片段进行标记,并为每个标记应用特定的样式;AnnotatedString 通常使用 buildAnnotatedString 方式进行创建对应的 AnnotatedString 对象;

  • 字符串构造方法:只需提供一个字符串作为参数,并使用 append 函数将该字符串添加到 AnnotatedString 中;
代码语言:txt
复制
val annotatedString1 = buildAnnotatedString {
    append("Compose Text ")
    append("通过 AnnotatedString 设置富文本效果!")
}
Text(text = annotatedString1)
  • 使用字符串和样式构建:允许在添加字符串时同时设置样式;使用 withStyle 函数包裹 append 函数,以便为特定的文本部分应用样式;
代码语言:txt
复制
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)
  • 使用字符串和注释构建点击操作:使用 addStringAnnotation 函数来添加注释,指定标记、注释数据以及注释的起始和结束位置,配合点击事件使用;其中点击事件,可以通过 TextModifier.clickable 进行监听,但问题是整个 Text 均可点击;若仅需要部分内容可以点击,可以使用 ClickableText
代码语言:txt
复制
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()
    }
})
12. inlineContent

    inlineContent 用于替换范围内容的映射存储;可以在 Text 中添加占位等操作;用于在文本中添加内联内容,并提供自定义的渲染逻辑;内联内容可以是特殊标记或占位符,用于在文本中插入自定义的组件或视图;

代码语言:txt
复制
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 = ""
        )
    })
)

    和尚对 TextAnnotatedString 的应理解和使用仅限于日常应用,对底层探究较少,如有错误,请多多指导!

阿策小和尚

【选题思路】

    Compose UI 发展至今,随着高性能和易用性,逐渐替换了历史的 Java / Kotlin 模式开发,而突然转向 Compose 开发,还是会有一段适应期,而 Text 作为最常用的组件,使用方式和场景有了更多的方式。和尚以此为出发点,事无巨细,详细介绍了 Text 的各类属性和效果图,同时对富文本的 AnnotatedString 进行了案例分析,帮助更多开发者更快的了解和应用。

【写作提纲】

  1. 介绍了 Text 的两类构造方法,对公共的属性进行的拆分介绍;
  2. 介绍了 Java / Kotlin 富文本的使用方式;
  3. 对比了 ComposeAnnotatedString 富文本的使用方式;
  4. Text 全属性进行了分析和拆解,并进行了案例尝试。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Text
    • 源码分析
      • 案例分析
        • 1. color & fontSize
        • 2. fontWeight & fontFamily
        • 3. letterSpacing & textDecoration
        • 4. textAlign
        • 5. lineHeight & maxLines
        • 6. overflow
        • 7. softWrap
        • 8. modifier
        • 9. fontStyle & style
        • 10. onTextLayout
        • 11. SpannableString & AnnotatedString
        • 12. inlineContent
    • 【选题思路】
    • 【写作提纲】
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档