-
-
Save stevdza-san/ff9dbec0e072d8090e1e6d16e6b73c91 to your computer and use it in GitHub Desktop.
import androidx.compose.foundation.text.ClickableText | |
import androidx.compose.runtime.Composable | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.platform.LocalUriHandler | |
import androidx.compose.ui.text.SpanStyle | |
import androidx.compose.ui.text.buildAnnotatedString | |
import androidx.compose.ui.text.font.FontWeight | |
import androidx.compose.ui.text.style.TextDecoration | |
import androidx.compose.ui.unit.TextUnit | |
@Composable | |
fun HyperlinkText( | |
modifier: Modifier = Modifier, | |
fullText: String, | |
linkText: List<String>, | |
linkTextColor: Color = Color.Blue, | |
linkTextFontWeight: FontWeight = FontWeight.Medium, | |
linkTextDecoration: TextDecoration = TextDecoration.Underline, | |
hyperlinks: List<String> = listOf("https://stevdza-san.com"), | |
fontSize: TextUnit = TextUnit.Unspecified | |
) { | |
val annotatedString = buildAnnotatedString { | |
append(fullText) | |
linkText.forEachIndexed { index, link -> | |
val startIndex = fullText.indexOf(link) | |
val endIndex = startIndex + link.length | |
addStyle( | |
style = SpanStyle( | |
color = linkTextColor, | |
fontSize = fontSize, | |
fontWeight = linkTextFontWeight, | |
textDecoration = linkTextDecoration | |
), | |
start = startIndex, | |
end = endIndex | |
) | |
addStringAnnotation( | |
tag = "URL", | |
annotation = hyperlinks[index], | |
start = startIndex, | |
end = endIndex | |
) | |
} | |
addStyle( | |
style = SpanStyle( | |
fontSize = fontSize | |
), | |
start = 0, | |
end = fullText.length | |
) | |
} | |
val uriHandler = LocalUriHandler.current | |
ClickableText( | |
modifier = modifier, | |
text = annotatedString, | |
onClick = { | |
annotatedString | |
.getStringAnnotations("URL", it, it) | |
.firstOrNull()?.let { stringAnnotation -> | |
uriHandler.openUri(stringAnnotation.item) | |
} | |
} | |
) | |
} |
If you have to use @StringRes:
@Composable
fun HyperlinkText(
modifier: Modifier = Modifier,
@StringRes fullTextResId: Int,
linksActions: List<String>,
hyperLinks: List<String>,
textStyle: TextStyle = TextStyle.Default,
linkTextColor: Color = Color.Blue,
linkTextFontWeight: FontWeight = FontWeight.Normal,
linkTextDecoration: TextDecoration = TextDecoration.None,
fontSize: TextUnit = TextUnit.Unspecified
) {
val fullText = LocalContext.current.getText(fullTextResId).toSpannable()
val annotations = fullText.getSpans(0, fullText.length, Annotation::class.java)
val annotatedString = buildAnnotatedString {
append(fullText)
linksActions.forEachIndexed { index, actionAnnotation ->
annotations?.find { it.value == actionAnnotation }?.let {
addStyle(
style = SpanStyle(
color = linkTextColor,
fontSize = fontSize,
fontWeight = linkTextFontWeight,
textDecoration = linkTextDecoration
),
start = fullText.getSpanStart(it),
end = fullText.getSpanEnd(it)
)
addStringAnnotation(
tag = "URL",
annotation = hyperLinks.get(index),
start = fullText.getSpanStart(it),
end = fullText.getSpanEnd(it)
)
}
addStyle(
style = SpanStyle(
fontSize = fontSize
),
start = 0,
end = fullText.length
)
}
}
val uriHandler = LocalUriHandler.current
ClickableText(
modifier = modifier,
text = annotatedString,
style = textStyle,
onClick = {
annotatedString
.getStringAnnotations("URL", it, it)
.firstOrNull()?.let { stringAnnotation ->
uriHandler.openUri(stringAnnotation.item)
}
}
)
}
Usage:
HyperlinkText(
modifier = Modifier.fillMaxWidth(),
fullTextResId = R.string.text_for_link,
linksActions = listOf("LINK"),
hyperLinks = listOf("https://google.com")
)
<string name="text_for_link">With text contain a <annotation type="LINK">link</annotation></string>
if you need a few links:
HyperlinkText(
modifier = Modifier.fillMaxWidth(),
fullTextResId = R.string.text_for_link,
linksActions = listOf("LINK1, LINK2"),
hyperLinks = listOf("https://google.com", "https://github.com")
)
<string name="text_for_link">With text contain a <annotation type="LINK1">link1</annotation> and you can paste <annotation type="LINK2">link2</annotation></string>
Why the onClick callback is never called using the top composable exemple:
@composable
fun HyperlinkText(
modifier: Modifier = Modifier,
fullText: String,
linkText: List,
linkTextColor: Color = Color.Blue,
linkTextFontWeight: FontWeight = FontWeight.Medium,
linkTextDecoration: TextDecoration = TextDecoration.Underline,
hyperlinks: List = listOf(),
fontSize: TextUnit = 24.sp
)
Cant find annotation key or value
This implementation as many others online does not work well with Accesibility. TalkBack is not reading each link individually and by that they are not triggered. I'm working on this and if I have a solution for it in Compose only i'll share it here. In the meantime you can maybe can be aware of it too :)
useful composable. Thanks.
It's best to wrap
uriHandler.openUri(stringAnnotation.item)
in atry-catch
because it throws exception if no activity is found to handle the intent.