Highlighting
About this widget
A great search interface highlights parts of the search results to explain why they are relevant to the user.
With InstantSearch Android, the Highlightable
interface and HighlightedString
objects simplify highlighting the right words in a search response that match your query.
You can read more about the concept of highlighting in the highlighting guide.
Examples
Lets take the example of an index containing movies. Each movie record consists of two fields: title and year.
Here is what the search engine response for a query "red"
could look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"title": "The Shawshank Redemption",
"year": 1994,
"genre": ["action", "adventure"],
"actors": ["Tim Robbins", "Morgan Freeman"],
"objectID": "439817390",
"_highlightResult": {
"title": {
"value": "The Shawshank <em>Red</em>emption",
"matchLevel": "full",
"fullyHighlighted": false,
"matchedWords": [
"red"
]
}
}
}
To display those movies in your interface, you likely have created a data class
that looks something like the following:
1
2
3
4
5
6
7
@Serializable
data class Movie(
val title: String,
val year: String,
val genre: List<String>,
override val objectID: ObjectID,
) : Indexable
Update it to add some highlighting. Implementing Highlightable
will deserialize the _highlightResult
for each movie, and make it available through the getHighlight{s}
methods. Create @Transient
attributes for each highlight to display, being either single values or lists:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Serializable
data class Movie(
val title: String,
val year: String,
val genre: List<String>,
override val objectID: ObjectID
override val _highlightResult: JsonObject?
) : Indexable, Highlightable {
@Transient
public val highlightedTitle: HighlightedString?
get() = getHighlight(Attribute("title"))
@Transient
public val highlightedGenres: List<HighlightedString>?
get() = getHighlights(Attribute("genre"))
@Transient
public val highlightedActors: List<HighlightedString>?
get() = getHighlights(Attribute("actors"))
}
Use these highlighted strings in the interface, for example in a MovieViewHolder
. There are three ways you can use a HighlightedString
:
Using highlighted strings in an Android view
- Directly as a
SpannedString
, with the highlight defaulting to bold:Copy1
TextUtils.concat(highlightedTitle?.toSpannedString(), " ($year)")
- As a customized
SpannedString
, specifying aParcelableSpan
to use as highlight style:Copy1 2
highlightedGenres?.toSpannedString(BackgroundColorSpan(Color.YELLOW)) ?: buildSpannedString { italic { append("unknown genre") } }
- Any way you want, iterating on
HighlightedString#tokens
to process it however you like:Copy1 2 3 4 5 6
// Displays actors with highlighted parts in uppercase highlightedActors?.joinToString { highlight -> highlight.tokens.joinToString("") { if (it.highlighted) it.content.uppercase() else it.content } }
Compose UI
- Directly as a
AnnotatedString
, with the highlight defaulting to bold:Copy1
highlightedTitle?.toAnnotatedString()
- As a customized
AnnotatedString
, specifying aSpanStyle
to use as highlight style:Copy1 2
highlightedGenres?.toAnnotatedString(SpanStyle(background = Color.Yellow)) ?: AnnotatedString("unknown genre", SpanStyle(fontStyle = FontStyle.Italic))
- Any way you want, iterating on
HighlightedString#tokens
to process it however you like:Copy1 2 3 4 5 6
// Displays actors with highlighted parts in uppercase highlightedActors?.joinToString { highlight -> highlight.tokens.joinToString("") { if (it.highlighted) it.content.uppercase() else it.content } }