Html 字符串可以非常方便的给文字添加样式,并且 compose-ui 在 1.7.0 版本后也提供了支持(1.7.0 以前无法在 compose 中使用)
在原生 View 系统中 HtmlCompat.fromHtml(...)
可以将 Html 字符串转换成 Spanned,本质上是解析少数的 html 标签为 Spannable,从而实现样式化文字。
但是 Compose 有自己的一套 AnnotatedString,并没有使用 View 系统中的 Spanned,所以无法直接使用 HtmlCompat.fromHtml
。
在 compose-ui 1.7.0 后提供了 AnnotatedString.fromHtml
可以实现相同的功能,而查看它的源码也比较简单,先使用 HtmlCompat.fromHtml
将 html 字符串转换成 View 系统中的 Spanned,然后再将 Spanned 转换成 Compose 中的 AnnotatedString。所以它们支持的标签和 css 属性基本是相同的: https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML
使用方式
在 strings.xml 中,可以使用 CDDATA 标签防止特殊字符转义,但是双引号和单引号还是需要加反斜杠。
res/strings.xml
:
<string name="agreement"><![CDATA[
请阅读我们的<a href=\"https://aitsuki.com\">隐私协议</a>和
<a href=\"https://aitsuki.com\">服务协议</a>
]]></string>
HtmlSample.kt
:
@Composable
fun Agreement() {
val annotatedString = AnnotatedString.fromHtml(
htmlString = stringResource(R.string.agreement),
linkStyles = TextLinkStyles(style = SpanStyle(color = Color.Blue)),
// 如果不添加Listener,模式是打开系统浏览器
linkInteractionListener = { linkAnnotation ->
val url = (linkAnnotation as LinkAnnotation.Url).url
// 打开App内的浏览器页面
// ...
}
)
Text(annotatedString)
}
使用 annotation 标签自定义样式
使用方式其实和 View 的 Spanned 差不多,官方有教程: https://developer.android.com/guide/topics/resources/string-resource#StylingWithAnnotations
fromHtml
可以通过 big
或 small
标签设置文字的相对大小,前者是增大 25%,后者是减小 20%,没办法精准控制。下面我们通过annotation
标签实现一个控制文字大小的功能。
strings.xml
:
<string name="greeting">
<![CDATA[
Hello, <annotation font-size="2em"><b>%1$s!</b></annotation>
]]>
</string>
HtmlText.kt
:
@Preview
@Composable
fun HtmlPreview() {
MaterialTheme {
HtmlText(stringResource(R.string.greeting, "Aitsuki"))
}
}
@Composable
fun HtmlText(htmlString: String) {
val annotatedString = AnnotatedString.fromHtml(htmlString)
// AnnotatedString是不可变对象,所以新建一个
val relativeSizeString = buildAnnotatedString {
append(annotatedString)
val annotations = annotatedString.getStringAnnotations(0, annotatedString.length)
for (annotation in annotations) {
// annotation.item = "2em"
// annotation.tag = "font-size"
if (annotation.tag == "font-size") {
// 这里可以判断文字大小单位,但是为了简单演示,仅支持em
val fontSizeEm =
annotation.item.substring(0, annotation.item.length - 2).toFloat()
addStyle(SpanStyle(fontSize = fontSizeEm.em), annotation.start, annotation.end)
}
}
}
Text(relativeSizeString)
}