如何在Jetpack Compose中突出显示文本中的特定单词?

10

我想知道如何在Jetpack Compose中突出显示文本的特定部分。 我尝试使用Html.fromHtml(),就像这样:

Text(text = Html.fromHtml(" <font color='red'> Hello </font> World").toString())

但是这并没有起作用。是否有任何方法可以在Compose中完成这个操作?

4个回答

19
你可以使用AnnotatedString来以多种样式显示文本。

例如:

类似:

Text(buildAnnotatedString {
    withStyle(style = SpanStyle(color = Color.Red)) {
        append("Hello")
    }
    append(" World ")
})

输入图像描述


1
如果我已经有一个字符串,并且想要从特定的索引到另一个特定的索引进行高亮,该怎么办? - Bugs Happen
@BugsHappen 请查看此链接 https://dev59.com/zcDqa4cB1Zd3GeqPXCel#73835998 - Ankit Verma

4

您可以使用AnnotatedString为每个单词/部分附加自己的样式,或在任何索引处添加不同的样式,这对于使用字符串资源非常有用。

对于hello world示例,您可以构建类似于以下内容的内容:


val annotatedString = buildAnnotatedString {
    val str = "Hello World" // or stringResource(id = R.string.hello_world)
    val boldStr = "Hello" // or stringResource(id = R.string.hello)
    val startIndex = str.indexOf(boldStr)
    val endIndex = startIndex + boldStr.length
    append(str)
    addStyle(style = SpanStyle(color = Color.Red), start = startIndex, end = endIndex)
}
Text(
    text = annotatedString,
)

Hello world text with the letters of hello in red

使用这种方式的addStyle允许我们做一些有趣的事情,比如将多个样式添加到同一段文本中。

val annotatedString = buildAnnotatedString {
    val str = "Hello Wonderful World" // or stringResource(id = R.string.hello_world)
    val boldStr = "Wonderful World" // or stringResource(id = R.string.world)
    val startIndex = str.indexOf(boldStr)
    val endIndex = startIndex + boldStr.length
    append(str)
    addStyle(style = SpanStyle(color = Color.Red), start = startIndex, end = endIndex)

    val italicsStr = "Wonderful"
    val italicsStartIndex = str.indexOf(italicsStr)
    val italicsEndIndex = startIndex + italicsStr.length
    addStyle(style = SpanStyle(fontStyle = FontStyle.Italic), start = italicsStartIndex, end = italicsEndIndex)
}
Text(
    text = annotatedString,
    style = TextStyle(fontWeight = FontWeight.Bold),
    color = Color.Blue,
)

The text hello wonderful world with varied color and italics


谢谢提到addStyle() - 我已经陷入了拆分和重新组装字符串的困境。当使用这种方法来突出显示自动完成选项时,最好在调用之前也检查if (startIndex > -1) - undefined

3
请检查下面的函数。在此,段落是您要处理的字符串源,而searchQuery则是您想要突出显示的特定文本。
这为您提供了文本和搜索高亮的动态状态。
@Composable
    fun getData(): StateFlow<AnnotatedString?> {

        val span = SpanStyle(
            color = MaterialTheme.colorScheme.onPrimaryContainer,
            fontWeight = FontWeight.SemiBold,
            background = MaterialTheme.colorScheme.primaryContainer
        )

        return combine(paragraph, searchQuery) { text, query ->
            buildAnnotatedString {
                var start = 0
                while (text.indexOf(query, start, ignoreCase = true) != -1 && query.isNotBlank()) {
                    val firstIndex = text.indexOf(query, start, true)
                    val end = firstIndex + query.length
                    append(text.substring(start, firstIndex))
                    withStyle(style = span) {
                        append(text.substring(firstIndex, end))
                    }
                    start = end
                }
                append(text.substring(start, text.length))
                toAnnotatedString()
            }
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
    }

1
受Matt Smith答案的启发,但以更可重用和灵活的方式进行,使用一个Pair列表来确定placeholderSpanStyle(如果需要其他内容,可以是自定义的data class...)。
然后迭代list,将相应的SpanStyle注释到它们的placeholder上,以此来注释string
@Composable
fun annotateRecursively(
    placeHolderList: List<Pair<String, SpanStyle>>,
    originalText: String
): AnnotatedString {
    var annotatedString = buildAnnotatedString { append(originalText) }
    for (item in placeHolderList) {
        annotatedString = buildAnnotatedString {
            val startIndex = annotatedString.indexOf(item.first)
            val endIndex = startIndex + item.first.length
            append(annotatedString)
            addStyle(style = item.second, start = startIndex, end = endIndex)
        }
    }
    return annotatedString
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接