AgentP
AgentP

Reputation: 7220

How to highlight specific word of the text in jetpack compose?

I wanted to know how to highlight the specific part of the text in jetpack compose. I tried Html.fromHtml() like this

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

But it didn't work. Is there any way I can do this in compose?

Upvotes: 12

Views: 8844

Answers (4)

Itoun
Itoun

Reputation: 3216

Inspired from Matt Smith's answer but in a more reusable and flexible way with taking a list of Pair precising the placeholder and a SpanStyle (instead of a Pair it can be a custom data class if you need anything else...)

Then iterate over the list to annotate the string with the corresponding SpanStyle to their placeholder.

@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
}

Upvotes: 4

Matt Smith
Matt Smith

Reputation: 955

You can use AnnotatedString to append each word/section with it's own style or to add different style at any index which is great if you're using a string resource.

For the hello world example you could construct something like this:


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

Using addStyle in this way allows us to do some fun things like adding multiple styles to the same text

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

Upvotes: 9

Gabriele Mariotti
Gabriele Mariotti

Reputation: 363637

You can use the AnnotatedString to display the text with multiple styles.

Something like:

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

enter image description here

Upvotes: 22

Ankit Verma
Ankit Verma

Reputation: 718

Check this function below. Here paragraph is your string source and searchQuery is the specific text you want to highlight.

This provides you a dynamic state for text and search highlights.

@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)
    }

Upvotes: 4

Related Questions