Skip to content

Instantly share code, notes, and snippets.

@slaviboy
Created September 16, 2022 21:27
Show Gist options
  • Save slaviboy/2da32bbed556d2232a8d9dddd510c143 to your computer and use it in GitHub Desktop.
Save slaviboy/2da32bbed556d2232a8d9dddd510c143 to your computer and use it in GitHub Desktop.
Jetpack Compose Text hyperlink some section of the text
data class Highlight(
val text: String,
val data: String,
val onClick: (data: String) -> Unit
)
@Composable
fun HighlightedText(
text: String,
highlights: List<Highlight>,
modifier: Modifier = Modifier,
) {
data class TextData(
val text: String,
val tag: String? = null,
val data: String? = null,
val onClick: ((data: AnnotatedString.Range<String>) -> Unit)? = null
)
val textData = mutableListOf<TextData>()
if (highlights.isEmpty()) {
textData.add(
TextData(
text = text
)
)
} else {
var startIndex = 0
highlights.forEachIndexed { i, link ->
val endIndex = text.indexOf(link.text)
if (endIndex == -1) {
throw Exception("Highlighted text mismatch")
}
textData.add(
TextData(
text = text.substring(startIndex, endIndex)
)
)
textData.add(
TextData(
text = link.text,
tag = "${link.text}_TAG",
data = link.data,
onClick = {
link.onClick(it.item)
}
)
)
startIndex = endIndex + link.text.length
if (i == highlights.lastIndex && startIndex < text.length) {
textData.add(
TextData(
text = text.substring(startIndex, text.length)
)
)
}
}
}
val annotatedString = buildAnnotatedString {
textData.forEach { linkTextData ->
if (linkTextData.tag != null && linkTextData.data != null) {
pushStringAnnotation(
tag = linkTextData.tag,
annotation = linkTextData.data,
)
withStyle(
style = SpanStyle(
color = infoLinkTextColor
),
) {
append(linkTextData.text)
}
pop()
} else {
append(linkTextData.text)
}
}
}
ClickableText(
text = annotatedString,
style = TextStyle(
fontSize = 0.031.sw,
fontWeight = FontWeight.Normal,
color = infoTextColor,
textAlign = TextAlign.Start
),
onClick = { offset ->
textData.forEach { annotatedStringData ->
if (annotatedStringData.tag != null && annotatedStringData.data != null) {
annotatedString.getStringAnnotations(
tag = annotatedStringData.tag,
start = offset,
end = offset,
).firstOrNull()?.let {
annotatedStringData.onClick?.invoke(it)
}
}
}
},
modifier = modifier
)
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
HighlightedText(
text = stringResource(id = R.string.disclaimer),
highlights = listOf(
Highlight(
text = stringResource(id = R.string.privacy_policy),
data = "https://stackoverflow.com/legal/privacy-policy",
onClick = { link ->
// do something with link
}
),
Highlight(
text = stringResource(id = R.string.terms_of_use),
data = "https://stackoverflow.com/legal/terms-of-use",
onClick = { link ->
// do something with link
}
)
)
)
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="disclaimer">By joining you agree to the privacy policy and terms of use.</string>
<string name="privacy_policy">privacy policy</string>
<string name="terms_of_use">terms of use</string>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment