本文已同步发表于我的
微信公众号
,搜索代码说
即可关注,欢迎与我沟通交流。
文章目录
一 什么是Span
Span
是功能强大的标记对象,可用于在字符
或段落
级别设置文本样式。通过将 Span
附加到文本对象,可以以各种方式更改文本,如修改颜色、使文本可单击、缩放文本大小以及以自定义方式绘制文本等。Span
还可以更改 TextPaint
属性,在Canvas
上绘制,甚至更改文本布局等。
二 创建并应用Span
TextView
通过 setText(CharSequence text)
设置文本,其中参数text
是CharSequence
类型,而 CharSequence
是处理 Span
富文本的顶层接口,如下:
CharSequence
是处理Span
的顶层接口;Spannable
接口继承自Spanned
,Spannable/Spanned
都可以获取Span
,此外Spannable
还可以设置Span
;SpannedString
、SpannableString
和SpannableStringBuilder
都是Android
中用于处理富文本的类,三者都直接或间接地实现了Spanned
接口;SpannableString、SpannableStringBuilder
还实现了Spannable
接口。
//Spanned接口
public interface Spanned extends CharSequence {
public <T> T[] getSpans(int start, int end, Class<T> type);
public int getSpanStart(Object tag);
public int getSpanEnd(Object tag);
public int getSpanFlags(Object tag);
public int nextSpanTransition(int start, int limit, Class type);
}
//Spannable接口
public interface Spannable extends Spanned {
public void setSpan(Object what, int start, int end, int flags);
public void removeSpan(Object what);
default void removeSpan(Object what, int flags) {
removeSpan(what);
}
}
可以看到
Spanned
接口中只有获取Span
信息的能力;Spannable
除了Spanned
的能力外,新增了setSpan
及removeSpan
的能力。
SpannedString、SpannableString、SpannableStringBuilder
三者之间的区别主要体现在可变性和数据结构上:
Class | 文本可变 | Span可变 | 数据结构 |
---|---|---|---|
SpannedString | N | N | 数组实现 |
SpannableString | N | Y | 数组实现 |
SpannableStringBuilder | Y | Y | 区间树 |
使用时三者应该如何选择:
- 如果在创建后不修改文本或
Span
,请使用SpannedString
; - 如果需要将少量
Span
附加到单个文本对象,并且文本本身是只读的,请使用SpannableString
; - 如果需要在创建后修改文本,并且需要将
Span
附加到文本,请使用SpannableStringBuilder
; - 如果需要将大量
Span
附加到文本对象,无论文本本身是否是只读的,请使用SpannableStringBuilder
。
2.1 setSpan(Object what, int start, int end, int flags)
setSpan()
将 Span
对象设置到文本中,从而修改文本状态,来看下方法中各个参数的含义:
- what : 要应用于文本中的从
start
到end
之间的Span
标记。 - start & end : 表明要应用范围的文本部分,默认是左闭右开区间,如
[start,end)
。 - flags: 应用
Span
后,如果在Span
边界(即在开始索引或结束索引处)内继续插入文本,则Span
会自动扩展以包含插入的文本,flags
参数决定Span
是否同步扩展以包含插入的文本。- Spanned.SPAN_INCLUSIVE_INCLUSIVE 包含两端端点,即左闭右闭区间
[start, end]
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 不包含两端
start
和end
所在的端点,即左开右开区间(start, end)
。 - Spanned.SPAN_INCLUSIVE_EXCLUSIVE 左闭右开区间,即
[start, end)
- Spanned.SPAN_EXCLUSIVE_INCLUSIVE 左开右闭区间,即
(start, end]
- Spanned.SPAN_INCLUSIVE_INCLUSIVE 包含两端端点,即左闭右闭区间
注意:flags
参数是在 setSpan()
之后,继续往文本中插入新文本时才会生效。上面的解释比较抽象,下面直接来看一个示例:
String myString = "01234";
int start = 1;
int end = 3;
int spanFlag = Spannable.SPAN_INCLUSIVE_INCLUSIVE; // this is what is changing
// set the span
SpannableStringBuilder spannableString = new SpannableStringBuilder(myString);
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
spannableString.setSpan(foregroundSpan, start, end, spanFlag);
// insert the text after the span has already been set
// (inserting at start index second so that end index doesn't get messed up)
spannableString.insert(end, "x");
spannableString.insert(start, "x");
textView.setText(spannableString);
设置不同得 flags
对应的不同结果:
flags使用 | 效果图 |
---|---|
setSpan()之后不再插入文本(默认) | ![]() |
以SPAN_INCLUSIVE_INCLUSIVE方式在边界插入文本 | ![]() |
以SPAN_INCLUSIVE_EXCLUSIVE方式在边界插入文本 | ![]() |
以SPAN_EXCLUSIVE_INCLUSIVE方式在边界插入文本 | ![]() |
以SPAN_EXCLUSIVE_EXCLUSIVE方式在边界插入文本 | ![]() |
示例来自:https://stackoverflow.com/questions/9879233/explain-the-meaning-of-span-flags-like-span-exclusive-exclusive
2.2 Span分类
Android
框架定义了在测量和渲染图形时检查的几个接口和抽象类。这些类具有允许 Span
访问 TextPaint
或者 Canvas
对象的方法。
在 android.text.style
包中提供了20多个 Span
,对主要的接口和抽象类进行了子类化。可以通过下面的方式进行分类:
Span
是否只改变外观
还是同时会改变文本测量/布局
(appearance vs metric
)Span
是在字符级别
或是在段落级别
影响文本 (character vs paragraph
)
2.2.1 影响字符级别的Span
a、影响外观的Span
- ForegroundColorSpan:字体颜色样式,用于改变字体颜色;
- BackgroundColorSpan:背景色样式,可以用来设定文本的背景色;
- UnderlineSpan:下划线样式,给文本添加下划线;
- MaskFilterSpan:应用指定的
MaskFilter
到文本,可以实现一些特殊的效果,如模糊(BlurMaskFilter
)等。
MaskFilterSpan(MaskFilter filter)设置滤镜Span,参数filter是MaskFilter类型。
BlurMaskFilter是MaskFilter子类,可以应用于Paint的一个滤镜。可以将绘制的图形进行模糊处理,以达到一些特定的视觉效果。
1、radius : 模糊半径。数值越大,模糊的范围越广。
2、style是BlurMaskFilter.Blur枚举,表示模糊的类型,包括以下四种:
— a、NORMAL:普通模糊,应用于整个图像。
— b、SOLID:只绘制模糊的区域,其他区域为透明。
— c、OUTER:外部模糊,只在图像外部模糊。
— d、INNER:内部模糊,只在图像内部模糊。
- StrikethroughSpan:删除线样式,给文本添加删除线效果;
- ClickableSpan:设置点击事件,注意点击相关的必须设置
textView.movementMethod = LinkMovementMethod.getInstance()
,否则点击不生效。 - URLSpan:点击文字时进行链接跳转。
b、影响测量的Span
设置 Span
之后的对象会重新测量文本以便于正确的布局和渲染。
- ScaleXSpan:在水平方向上缩放文本;
- RelativeSizeSpan:相对大小样式,相对于原始大小调整文本的大小;
- AbsoluteSizeSpan:绝对大小样式,以指定的像素大小设置文本的大小;
- SubscriptSpan:将文本
baseline
的位置向下移动,用于显示下标文本; - SuperscriptSpan:将文本
baseline
的位置向上移动,用于显示上标文本; - StyleSpan:字体样式,如通过传入的
Typeface
设置正常、粗体、斜体、加粗并倾斜等样式; - TextAppearanceSpan:设置文本字体、大小、样式和颜色等
- ImageSpan:图片样式,用于在文本中插入图片;
- TypefaceSpan:设置不同样式的文本字体
2.2.2 影响段落级别的Span
- LeadingMarginSpan:段落缩进,即为文本添加指定的行首缩进;
- LineBackgroundSpan:改变行的背景色,如果
[start,end)
不够一行按一行处理; - LineHeightSpan:改变段落的行高,可用于调整行与行之间的间距。注意,
LineHeightSpan
改变的是整个段落的行高,即使它只覆盖段落的一部分。 - DrawableMarginSpan:在文本的行首添加一个带有指定边距的
Drawable
; - IconMarginSpan:
IconMarginSpan与DrawableMarginSpan
类似,在文本的行首添加一个带有指定边距的Bitmap
; - QuoteSpan:
QuoteSpan
可以在文本开始的地方添加引用样式(一个垂直的线条); - BulletSpan:给文本添加项目符号(圆点样式);
- AlignmentSpan:文本对齐方式,
AlignmentSpan.Standard
是其默认实现,其构造函数AlignmentSpan.Standard(Layout.Alignment align)
中的
align
是Layout.Alignment
枚举类型参数,包括三种情况:ALIGN_CENTER居中、ALIGN_NORMAL正常、ALIGN_OPPOSITE相反方向。 - TabStopSpan:段落中第一行的左侧偏移量(与
\t
有关系),默认实现为TabStopSpan.Standard
。
三 自定义Span
通过自定义 Span
可以实现更多样化的文本样式和交互效果。根据自定义的Span
影响文本是在字符级别还是段落级别,以及影响的是布局还是外观,可以参考下表:
场景 | 类或接口 |
---|---|
字符级别影响文本 | CharacterStyle |
段落级别影响文本 | ParagraphStyle |
影响文本外观 | UpdateAppearance |
影响文本布局 | UpdateLayout |
不过大部分场景下,我们不用继承到这么深的父类,选择合适的已有Span
或者继承 ReplacementSpan
进行扩展是一个不错的选择。
可能需要自定义 Span
的一些常见场景:
- 定制文本样式:
Android
提供了一些内置的Span
类,如ForegroundColorSpan
、BackgroundColorSpan
和StyleSpan
,但有时可能需要更多样化的文本样式,例如自定义字体、阴影效果等。通过自定义Span
,可以根据需要实现特定的文本样式效果。 - 添加交互性:某些情况下,可能需要在文本中添加交互性,例如可点击的链接、点击事件等。
ClickableSpan
是一个内置的可点击Span
,可以根据自己的需求自定义更复杂的交互行为,例如自定义点击效果、处理特定的点击事件等。 - 自定义图文混排:
ImageSpan
可用于在文本中插入图片,但有时可能需要更复杂的图文混排效果,例如在文本中插入带有特殊样式的图标、自定义图标与文本的对齐方式等。通过自定义Span
,可以实现更灵活的图文混排效果。 - 特定的文本效果:在某些情况下,可能需要特定的文本效果,例如在文本中添加背景色、下划线、删除线、引用样式等。通过自定义
Span
,可以根据具体需求实现这些特定的文本效果。 - 自定义文本布局:
LeadingMarginSpan
可以用于段落缩进,但在某些情况下,可能需要更复杂的文本布局,例如为文本添加自定义的间距、设置行高等。通过自定义Span
,可以实现更高级的文本布局效果。
四 小结
本文主要是介绍了一些理论知识,包括Span
的定义、分类、自定义等,因为篇幅原因,Span
的使用示例放在下一篇中。
五 资料
【1】https://developer.android.google.cn/develop/ui/views/text-and-emoji/spans
【2】https://medium.com/androiddevelopers/spantastic-text-styling-with-spans-17b0c16b4568
【3】[译] 论 Android 中 Span 的正确打开方式