Android 开发中经常会有对 TextView 的局部文字样式做单独处理的需求,比如针对某些文字进行加粗或是改变颜色等等。

这种处理在 Web 开发中十分简单,HTML 的标签能够很方便的进行调整。但是在 Android 开发中,却要稍微麻烦一些,我们来看几种处理方式。

TextView 拼接

利用多个 TextView 拼接的方式,我们可以针对不同的 TextView 样式进行调整,这种方法似乎跟 HTML 标签有点类似,但实际上却相去甚远。

当你有多个不连续的文字需要使用同一种样式的时候,你就需要建立多个具有相同样式的 TextView,从而对同一个句子或段落形成割裂感。

另一方面,你甚至还需要处理好每一个 TextView 的 Padding 以及 Margin 边距属性以及适当的换行等,否则 UI 就会乱得惨不忍睹。

总体来说,多 TextView 拼接来处理同一段文字是非常不推荐的方法。

利用 HTML 标签设置样式

既然 HTML 在处理这种需求有极大的优势,那么我们能不能使用类似的处理方式呢?

Android SDK 中其实为我们提供了直接解析 HTML 的方法,所以只要将字符串包装成 HTML 代码即可实现局部样式修改,比如:

TextView textView = findViewById(R.id.sign_up_link);
textView.setText(Html.fromHtml("No account yet? <font color='#FFFFFF'><big>Create one</big></font>."));

利用 Html.fromHtml() 方法可以将字符串解析成 HTML 样式,再像平时一样使用的 setText() 方法即可把文字加载到 TextView 中去。

效果如下:

载入 HTML 效果图

可以看到,底部的那行小字对后半句做了加粗以及颜色调整。

利用 HTML 设置样式十分方便,解决了多 TextView 拼接的割裂感,但它依然存在问题。

你可以看到我的示例代码中使用了 <big> 标签来设置字体的大小,其实在常规的 HTML 代码中,我更加倾向于使用 <font> 标签的 size 属性来进行调整,因为可以减少标签嵌套。

但是我在这里使用 <big> 标签的原因是,Html.fromHtml() 方法无法解析 size 属性,我们可以看看源码:

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    handleStartTag(localName, attributes);
}

private void handleStartTag(String tag, Attributes attributes) {
    if (...) {
        ...
    } else if (...) {
        ...
    } else if (tag.equalsIgnoreCase("font")) {
        startFont(mSpannableStringBuilder, attributes);
    } else if (...) {
        ...
    }
}

private void startFont(Editable text, Attributes attributes) {
    String color = attributes.getValue("", "color");
    String face = attributes.getValue("", "face");
    if (!TextUtils.isEmpty(color)) {
        int c = getHtmlColor(color);
        if (c != -1) {
            start(text, new Foreground(c | 0xFF000000));
        }
    }
    if (!TextUtils.isEmpty(face)) {
        start(text, new Font(face));
    }
}

可以看到,对于 <font> 标签,只解析 colorface 两个属性,其他属性比如刚提到的 size 并没有进行解析,自然也就不可以通过其设置字体大小了。

这里仅仅拿了 <font> 标签进行举例,其他标签也有类似的情况,也就是说 Android 的 Html.fromHtml() 方法并没有办法对所有的 HTML 样式进行解析。

利用 SpannableString 设置样式

其实 Android SDK 也为我们提供了修改 TextView 局部样式的方法,利用 SpannableString 也可以实现类似的功能:

SpannableString spannableString = new SpannableString("Hello World!");
BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(Color.RED);
spannableString.setSpan(backgroundColorSpan, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView textView = findViewById(R.id.tv);
textView.setText(spannableString);

这是 TextView 的 Span 样式类,首先在 SpannableString 对象中传入要显示的内容,调用 setSpan() 方法实现字符串各种风格的显示。

setSpan() 方法有 4 个参数。第一个参数表示格式,可以是前景色、背景色等,这里指定其背景色为红色;第二个参数是格式开始的 Index;第三个参数是格式结束的 Index;第四个参数是一个常量,有以下 4 个值:

  • Spannable.SPAN_INCLUSIVE_EXCLUSIVE:前面包括,后面不包括。即在文本前插入新文本会应用该样式,而在文本后插入新文本不会应用该样式。
  • Spannable.SPAN_INCLUSIVE_INCLUSIVE:前面包括,后面包括。即在文本前插入新文本会应用该样式,在文本后插入新文本也会应用该样式。
  • Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前面不包括,后面不包括。即在文本前插入新文本不会应用该样式,在文本后插入新文本也不会应用该样式。
  • Spannable.SPAN_EXCLUSIVE_INCLUSIVE:前面不包括,后面包括。即在文本前插入新文本不会应用该样式,而在文本后插入新文本会应用该样式。

最后也是调用 TextViewsetText() 方法把这个 SpannableString 对象设置进去,效果如下:

Span 样式修改背景颜色

使用 SpannableString 不仅可以设置局部样式,还支持设置局部点击事件,这点比利用 HTML 标签设置样式更加灵活。