插图由 Virginia Poltrack(https://twitter.com/VPoltrack) 绘制
探索 Android 中的 Span
在 Android 中,使用 Span 定义文本的样式. 通过 Span 改变几个文字的颜色,让它们可点击,放缩文字的大小甚至是绘制自定义的项目符号点(bullet points,国外人名中名字之间的间隔符号 · ,HTML 中无序列表项的默认符号)。Span 能够改变 TextPaint 属性,在 Canvas 上绘制,甚至是改变文本的布局和影响像行高这样的元素。Span 是可以附加到文本或者从本文分离的标记对象(markup objects);它们可以被应用到部分或整段的文本中。
让我们来看看Span如何使用、提供了哪些开箱即用的功能、怎样简单地创建我们自己的 Span 以及如何使用和测试它们。
在 Android 上定义文本样式
Android 提供了几种定义文本样式的方法:
单一样式 —— 样式应用在 TextView 显示的整个文本
多重样式 —— 多种样式应用在字符或者段落级别的文本
单一样式 使用 XML 属性或者 样式和主题 引入了 TextView 的所有内容的样式。这种方式实现简单,通过 XML 即可实现,但是并不能只定义部分内容的样式。举个例子,通过设置 textStyle=”bold”,所有的文本都会变为黑体;你不能只定义特定的几个字符为黑体。
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="32sp"
android:textStyle="bold"/>
多重样式 引入了给一段文本添加多种样式的功能。例如,一个单词斜体而另一个粗体。多重样式可以通过使用 HTML 标签、 Span 或者是在 Canvas 上处理自定义的文本绘制。
左图:单一样式文本。设置了 textSize=”32sp” 和 textStyle=”bold” 的 TextView 。右图:多重样式文本。设置了 ForegroundColorSpan, StyleSpan(ITALIC), ScaleXSpan(1.5f), StrikethroughSpan 的文本。
左图:单一样式文本。设置了 textSize=”32sp” 和 textStyle=”bold” 的 TextView 。右图:多重样式文本。设置了 ForegroundColorSpan, StyleSpan(ITALIC), ScaleXSpan(1.5f), StrikethroughSpan 的文本。
HTML 标签是解决简单问题的简单办法,例如使文本加粗、斜体,甚至是显示项目符号点。为了展示含有 HTML 标签的文本,使用 Html.fromHtml 方法。在内部实现时,HTML 标签被转换成了 span 。但是请注意,Html 类并不支持完整的 HTML 标签和 CSS 样式,例如将小黑点改为其他的颜色。
val text = "My text
myTextView.text = Html.fromHtml(text)
当你有文本样式的需求,但是 Android 平台默认不支持时,你还可以手动地在 Canvas 上绘制文本,例如让文字弯曲排布。
Span 允许你实现具有更细粒度自定义的多重样式文本。举个例子,通过 BulletSpan,你可以定义你的段落文本拥有项目符号点。你可以定制文本和点号之间的间距和点号的颜色。从 Android P 开始,你甚至可以 设置点号的半径 。你也可以创建 span 的自定义实现。在文章中查看 “创建自定义 span” 部分可以找到如何实现。
val spannable = SpannableString("My text \nbullet one\nbullet two")
spannable.setSpan(
BulletPointSpan(gapWidthPx, accentColor),
/* 起始索引 */ 9, /* 终止索引 */ 18,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(
BulletPointSpan(gapWidthPx, accentColor),
/* 起始索引 */ 20, /* 终止索引 */ spannable.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
myTextView.text = spannable
左图:使用 HTML 标签;中图:使用 BulletSpan,默认圆点大小;右图:在 Android P 上使用 BulletSpan 或者自定义实现。
左图:使用 HTML 标签;中图:使用 BulletSpan,