You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何将字符串格式化与Annotation结合实现多字体TextView?

解决方案:动态格式化字符串+自定义字体分段设置

这个问题确实挺常见的——官方标注(annotation)仅支持静态字符串,而Html.fromHtml()又不支持自定义字体,不过咱们有几个靠谱的办法能实现你的需求,让到期日期用Montserrat SemiBold,其余文本用Regular:

方法1:用SpannableStringBuilder手动定位并设置字体

这种方法适合保留原单个字符串资源的场景,核心思路是先完成字符串格式化,再找到动态插入的日期位置,给该段文本单独设置字体:

Kotlin 代码示例(直接使用)

val expiryDate = "Apr 9, 2020"
// 先完成字符串格式化
val fullText = context.resources.getString(R.string.description, expiryDate)
// 定位到期日期在完整文本中的位置
val dateStartIndex = fullText.indexOf(expiryDate)
val dateEndIndex = dateStartIndex + expiryDate.length

// 创建Spannable对象并设置字体样式
val spannable = SpannableStringBuilder(fullText)
val semiBoldTypeface = ResourcesCompat.getFont(context, R.font.montserrat_semibold)
spannable.setSpan(
    TypefaceSpan(semiBoldTypeface),
    dateStartIndex,
    dateEndIndex,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)

// 给TextView设置最终的富文本
yourTextView.text = spannable

适配DataBinding的版本(BindingAdapter)

如果想用DataBinding直接绑定,可以写一个自定义BindingAdapter,这样在XML里就能直接用:

@BindingAdapter("formattedTextWithCustomFont")
fun TextView.setFormattedTextWithCustomFont(textRes: Int, dynamicParam: String) {
    val fullText = context.resources.getString(textRes, dynamicParam)
    val paramStartIndex = fullText.indexOf(dynamicParam)
    val paramEndIndex = paramStartIndex + dynamicParam.length
    
    val spannable = SpannableStringBuilder(fullText)
    val semiBoldTypeface = ResourcesCompat.getFont(context, R.font.montserrat_semibold)
    spannable.setSpan(
        TypefaceSpan(semiBoldTypeface),
        paramStartIndex,
        paramEndIndex,
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
    )
    
    this.text = spannable
}

然后在XML中调用:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:formattedTextWithCustomFont="@{@string/description, expiry_date}" />

方法2:拆分字符串资源,分段拼接富文本

如果担心indexOf找不到动态文本(比如极端情况下文本重复),可以把原字符串拆成三个静态资源,分别拼接并设置字体,这种方法更稳妥:

第一步:拆分字符串资源

<string name="description_prefix">Your trial ends on </string>
<string name="description_suffix">. You will be charged after your trial ends </string>

第二步:分段拼接并设置字体

val expiryDate = "Apr 9, 2020"
val prefix = SpannableStringBuilder(context.resources.getString(R.string.description_prefix))
// 给到期日期设置半粗体
val dateSpan = SpannableString(expiryDate).apply {
    setSpan(
        TypefaceSpan(ResourcesCompat.getFont(context, R.font.montserrat_semibold)),
        0,
        length,
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
    )
}
val suffix = SpannableStringBuilder(context.resources.getString(R.string.description_suffix))

// 拼接成完整富文本
val fullSpannable = prefix.append(dateSpan).append(suffix)
yourTextView.text = fullSpannable

这两种方法都能完美避开官方annotation和Html.fromHtml的限制,实现你想要的自定义字体分段效果。

内容的提问来源于stack exchange,提问作者Karthikeyan Subramanian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 09:37:27