Compose中 buildAnnotatedString的使用:

0 阅读3分钟

一、核心含义与作用

buildAnnotatedString 是 Jetpack Compose 中专门用于构建带样式 / 标记的富文本字符串的核心函数,你可以把它理解为「Compose 版的 SpannableString(Android 传统富文本)」,但用法更简洁、更符合 Compose 的声明式风格。

  • 字面意思build(构建) + Annotated(带注解 / 标记的) + String(字符串),即「构建一个带标记 / 样式的字符串」。
  • 核心价值:突破普通 String 只能单一样式的限制,让一段文本中不同部分拥有不同样式(如部分文字变色、加粗、下标),或给部分文本添加自定义标记(如可点击的文本区域、跳转链接)。
  • 使用场景:富文本显示、可点击的文本片段(如隐私政策)、带特殊格式的文本(如数学公式、金额)。

二、基础用法:核心语法

buildAnnotatedString 是一个构建器函数,内部通过链式调用的方式拼接文本和样式,核心语法如下:

@Composable
fun BuildAnnotatedStringBasic() {
    // 1. 构建带注解的字符串
    val annotatedText = buildAnnotatedString {
        // ① 追加普通文本
        append("普通文本")

        // ② 追加带样式的文本(withStyle + SpanStyle)
        withStyle(
            style = SpanStyle( // 文本样式:颜色、字号、粗细等
                color = Color.Red,
                fontWeight = FontWeight.Bold,
                fontSize = 18.sp
            )
        ) {
            append("带样式的文本") // 这个范围内的文本会应用上述样式
        }

        // ③ 继续追加普通文本(样式回到默认)
        append("普通文本")
    }

    // 2. 用 Text 组件显示
    Text(text = annotatedText, modifier = Modifier.padding(16.dp))
}

三、关键 API 详解

buildAnnotatedString 内部支持的核心方法,按用途分为两类:

1. 样式相关(修改文本外观)

方法作用
append(text: String)追加普通文本(无样式)
withStyle(style: SpanStyle)给代码块内的文本应用样式(SpanStyle 是文本样式的封装)
SpanStyle文本样式类,支持 color、fontSize、fontWeight、baselineShift(下标 / 上标)、textDecoration(下划线 / 删除线)等

2. 标记相关(给文本加自定义注解,用于交互)

方法作用
pushStringAnnotation(tag: String, annotation: String)给后续文本添加「字符串标记」(key-value 形式),用于识别可点击区域
pop()结束当前标记 / 样式的作用范围(必须和 push/pushStyle 成对出现)

四、典型示例:带点击标记的富文本

这是 buildAnnotatedString 最常用的场景(如隐私政策、用户协议),结合 ClickableText 实现部分文本点击:

@Composable
fun AnnotatedStringWithClick() {
    // 1. 构建带点击标记的 AnnotatedString
    val policyText = buildAnnotatedString {
        append("注册即同意")

        // ① 给「隐私政策」添加标记
        pushStringAnnotation(
            tag = "PRIVACY", // 标记标签(自定义,用于识别)
            annotation = "隐私政策链接" // 标记内容(可传链接、ID 等)
        )
        // ② 给标记文本加样式(蓝色、下划线)
        withStyle(
            style = SpanStyle(
                color = Color.Blue,
                textDecoration = TextDecoration.Underline
            )
        ) {
            append("隐私政策")
        }
        pop() // ③ 结束标记和样式(必须调用,否则后续文本都会带标记)

        append("和")

        // 同理,给「用户协议」加标记
        pushStringAnnotation(tag = "USER", annotation = "用户协议链接")
        withStyle(
            style = SpanStyle(color = Color.Blue, textDecoration = TextDecoration.Underline)
        ) {
            append("用户协议")
        }
        pop()
    }

    // 2. 用 ClickableText 显示(支持点击事件)
    ClickableText(
        text = policyText,
        onClick = { offset ->
            // 根据点击位置,获取对应的标记
            policyText.getStringAnnotations(
                tag = "PRIVACY",
                start = offset,
                end = offset
            ).firstOrNull()?.let {
                // 点击了「隐私政策」
                println("点击了:隐私政策")
            }

            policyText.getStringAnnotations(
                tag = "USER",
                start = offset,
                end = offset
            ).firstOrNull()?.let {
                // 点击了「用户协议」
                println("点击了:用户协议")
            }
        },
        modifier = Modifier.padding(16.dp)
    )
}

五、关键注意事项

  1. 成对调用 push/pop:使用 pushStringAnnotation 或 pushStyle 后,必须调用 pop() 结束作用范围,否则后续所有文本都会继承该标记 / 样式。

  2. 仅支持 Text/ClickableTextAnnotatedString 只能传给 Text 或 ClickableText 的 text 参数,不能直接作为普通字符串使用。

  3. 样式优先级withStyle 内部的样式会覆盖外部的默认样式,且内层样式会覆盖外层样式。

  4. 性能优化:如果 AnnotatedString 内容固定,用 remember 缓存,避免每次重组重新构建:

val cachedText = remember {
  buildAnnotatedString { /* 构建逻辑 */ }
}