超能力文本:探索Span机制的多彩世界(一)

本文已同步发表于我的微信公众号,搜索 代码说 即可关注,欢迎与我沟通交流。

一 什么是Span

Span 是功能强大的标记对象,可用于在字符段落级别设置文本样式。通过将 Span 附加到文本对象,可以以各种方式更改文本,如修改颜色、使文本可单击、缩放文本大小以及以自定义方式绘制文本等。Span 还可以更改 TextPaint 属性,在Canvas上绘制,甚至更改文本布局等。

二 创建并应用Span

TextView 通过 setText(CharSequence text) 设置文本,其中参数textCharSequence类型,而 CharSequence 是处理 Span 富文本的顶层接口,如下:

创建Span

  • CharSequence是处理Span的顶层接口;
  • Spannable接口继承自SpannedSpannable/Spanned都可以获取 Span,此外Spannable还可以设置 Span
  • SpannedStringSpannableStringSpannableStringBuilder 都是 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的能力外,新增了setSpanremoveSpan的能力。

SpannedString、SpannableString、SpannableStringBuilder三者之间的区别主要体现在可变性和数据结构上

Class文本可变Span可变数据结构
SpannedStringNN数组实现
SpannableStringNY数组实现
SpannableStringBuilderYY区间树

使用时三者应该如何选择:

  • 如果在创建后不修改文本或Span,请使用 SpannedString
  • 如果需要将少量Span附加到单个文本对象,并且文本本身是只读的,请使用SpannableString
  • 如果需要在创建后修改文本,并且需要将Span附加到文本,请使用SpannableStringBuilder
  • 如果需要将大量Span附加到文本对象,无论文本本身是否是只读的,请使用SpannableStringBuilder

2.1 setSpan(Object what, int start, int end, int flags)

setSpan()Span 对象设置到文本中,从而修改文本状态,来看下方法中各个参数的含义:

  • what : 要应用于文本中的从 startend 之间的 Span标记。
  • start & end : 表明要应用范围的文本部分,默认是左闭右开区间,如 [start,end)
  • flags: 应用Span后,如果在Span边界(即在开始索引或结束索引处)内继续插入文本,则Span会自动扩展以包含插入的文本,flags参数决定Span是否同步扩展以包含插入的文本。
    • Spanned.SPAN_INCLUSIVE_INCLUSIVE 包含两端端点,即左闭右闭区间[start, end]
    • Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 不包含两端startend所在的端点,即左开右开区间(start, end)
    • Spanned.SPAN_INCLUSIVE_EXCLUSIVE 左闭右开区间,即[start, end)
    • Spanned.SPAN_EXCLUSIVE_INCLUSIVE 左开右闭区间,即(start, end]

注意: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()之后不再插入文本(默认)origin
SPAN_INCLUSIVE_INCLUSIVE方式在边界插入文本SPAN_INCLUSIVE_INCLUSIVE
SPAN_INCLUSIVE_EXCLUSIVE方式在边界插入文本SPAN_INCLUSIVE_EXCLUSIVE
SPAN_EXCLUSIVE_INCLUSIVE方式在边界插入文本SPAN_EXCLUSIVE_INCLUSIVE
SPAN_EXCLUSIVE_EXCLUSIVE方式在边界插入文本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)

Span categories: character vs paragraph, appearance vs metric

2.2.1 影响字符级别的Span
a、影响外观的Span

Appearance affecting spans

  • 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 之后的对象会重新测量文本以便于正确的布局和渲染。

Metric affecting spans

  • ScaleXSpan:在水平方向上缩放文本;
  • RelativeSizeSpan:相对大小样式,相对于原始大小调整文本的大小;
  • AbsoluteSizeSpan:绝对大小样式,以指定的像素大小设置文本的大小;
  • SubscriptSpan:将文本baseline的位置向下移动,用于显示下标文本;
  • SuperscriptSpan:将文本baseline的位置向上移动,用于显示上标文本;
  • StyleSpan:字体样式,如通过传入的 Typeface 设置正常、粗体、斜体、加粗并倾斜等样式;
  • TextAppearanceSpan:设置文本字体、大小、样式和颜色等
  • ImageSpan:图片样式,用于在文本中插入图片;
  • TypefaceSpan:设置不同样式的文本字体

2.2.2 影响段落级别的Span

Paragraph affecting spans

  • LeadingMarginSpan:段落缩进,即为文本添加指定的行首缩进;
  • LineBackgroundSpan:改变行的背景色,如果[start,end)不够一行按一行处理;
  • LineHeightSpan:改变段落的行高,可用于调整行与行之间的间距。注意,LineHeightSpan改变的是整个段落的行高,即使它只覆盖段落的一部分。
  • DrawableMarginSpan:在文本的行首添加一个带有指定边距的 Drawable
  • IconMarginSpanIconMarginSpan与DrawableMarginSpan类似,在文本的行首添加一个带有指定边距的 Bitmap
  • QuoteSpanQuoteSpan 可以在文本开始的地方添加引用样式(一个垂直的线条);
  • BulletSpan:给文本添加项目符号(圆点样式);
  • AlignmentSpan:文本对齐方式,AlignmentSpan.Standard是其默认实现,其构造函数AlignmentSpan.Standard(Layout.Alignment align) 中的
    alignLayout.Alignment枚举类型参数,包括三种情况:ALIGN_CENTER居中、ALIGN_NORMAL正常、ALIGN_OPPOSITE相反方向
  • TabStopSpan:段落中第一行的左侧偏移量(与\t有关系),默认实现为TabStopSpan.Standard

三 自定义Span

通过自定义 Span 可以实现更多样化的文本样式和交互效果。根据自定义的Span影响文本是在字符级别还是段落级别,以及影响的是布局还是外观,可以参考下表:

场景类或接口
字符级别影响文本CharacterStyle
段落级别影响文本ParagraphStyle
影响文本外观UpdateAppearance
影响文本布局UpdateLayout

不过大部分场景下,我们不用继承到这么深的父类,选择合适的已有Span或者继承 ReplacementSpan 进行扩展是一个不错的选择。

可能需要自定义 Span的一些常见场景:

  1. 定制文本样式Android 提供了一些内置的 Span 类,如 ForegroundColorSpanBackgroundColorSpanStyleSpan,但有时可能需要更多样化的文本样式,例如自定义字体、阴影效果等。通过自定义 Span,可以根据需要实现特定的文本样式效果。
  2. 添加交互性:某些情况下,可能需要在文本中添加交互性,例如可点击的链接、点击事件等。ClickableSpan 是一个内置的可点击 Span,可以根据自己的需求自定义更复杂的交互行为,例如自定义点击效果、处理特定的点击事件等。
  3. 自定义图文混排ImageSpan 可用于在文本中插入图片,但有时可能需要更复杂的图文混排效果,例如在文本中插入带有特殊样式的图标、自定义图标与文本的对齐方式等。通过自定义 Span,可以实现更灵活的图文混排效果。
  4. 特定的文本效果:在某些情况下,可能需要特定的文本效果,例如在文本中添加背景色、下划线、删除线、引用样式等。通过自定义 Span,可以根据具体需求实现这些特定的文本效果。
  5. 自定义文本布局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 的正确打开方式

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_小马快跑_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值