【Android】Span富文本简介
一,概述
android.text包下span体系类,主要指Spanned、Spannable、ParagraphStyle、CharacterStyle实现类。Android通过Span体系,搭建了富文本API,其中Spanned、Spannable实现了CharSequence接口,旨在映射段落start~end之间Span装饰对象。Span对象大多实现ParagraphStyle、CharacterStyle两者之一,是装饰段落字符的对象,其中ParagraphStyle会影响layout,CharacterStyle只影响draw期间paint样式。
除此之外还存在MovementMethod接口代理解析touch事件。
以及Html工具类,通过html语言映射到原生Span接口。如下,
Html.fromHtml("<p>这是</p>", Html.FROM_HTML_MODE_LEGACY)
Spannable核心实现类是SpannableStringBuilder,该类实现了setSpan、getSpan、removeSpan等方法,
通过map、数组,保存了段落到Span对象的映射。
Span对象ParagraphStyle、CharacterStyle实现类在android.text.style包下,如下,读者可自行查看
每个Span影响了字符样式或者段落位置,甚至可单独添加点击事件,如ClickableSpan。
二,实例
如下,简单对TextView设置一个下划线+可点击Span例子。
findViewById<TextView>(R.id.span_text).apply {val spstr = SpannableStringBuilder("这是一段文字")spstr.setSpan(UnderlineSpan(),0,2, Spannable.SPAN_INCLUSIVE_INCLUSIVE)spstr.setSpan(object : ClickableSpan(){override fun onClick(widget: View) {Toast.makeText(this@SpanTestActivity,"Click ",Toast.LENGTH_SHORT).show()}override fun updateDrawState(ds: TextPaint) {super.updateDrawState(ds)ds.color = Color.BLUEds.isUnderlineText = false}},2,5, Spannable.SPAN_INCLUSIVE_INCLUSIVE)movementMethod = LinkMovementMethod.getInstance()highlightColor = Color.TRANSPARENTtext = spstr}
三,分析
1 ,setSpan
实现类是SpannableStringBuilder,在setSpan方法中,通过map、数组,将Span和段落之间现成映射,并且同一个Span对象不可复用在多个段落,以下逻辑确保了传入同一个Span时会更新start、end、flag数组,
跟进,如果传入一个新的Span对象,会把欧尼到mSpan*数组中,如下
通过GrowingArrayUtils#append工具类方法,确保了Span被安全添加至数组中,
调用restorInvariants方法,会重建索引树,即mIndexOfSpan。
明白了setSpan,那么getSpan以及removeSpan,便可知道其实现逻辑,不赘述了。
2,CharacterStyle
对于字符样式,在Android中是通过Paint控制,因此CharacterStyle有一个必须实现的接口方法,
CharacterStyle#updateDrawState,子类通过重写此模版方法,便可改变Paint属性,如Color等。读者可自定义一个Span实现CharacterStyle接口,便可做出更多自定义行为。
以ForegroundColorSpan为例,其updateDrawState方法调用了Paint#setColor而已,不赘述。
那么updateDrawState何时被调用呢?显而易见,在TextView#draw期间,通过dubug stack可清晰知道
3,ClickableSpan
ClickableSpan是对CharacterStyle接口实现的抽象类,新增了onClick方法,只在自定段落start~end被点击后,触发。
要实现ClickableSpan效果,还需要传递MovementMethod接口实现类给到TextView,通过setMovementMethod方法即可,
MovementMethod接口hook了touch事件过程,可逐个对字符实现touch事件分发。LinkMovementMethod实现类新增解析ClickableSpan逻辑,即可触发其onClick方法,大致逻辑如下
touch事件识别选中的start和end,通过getSpans方法获得Span,如果存在ClickableSpan且为1情况下,触发onClick,否则忽略。
4,ParagraphStyle
ParagraphStyle拓展接口如下,均和Layout过程相关,在layout期间确立了边距,随后也会在draw期间调用draw相关模版方法,实现绘制drawable等操作,以DrawableMarginSpan为例
以上,即实现了文本中插入了drawble操作。
5,Html
对于简单富文本,通过setSpan方法还是稍许复杂,且可读性不高,因此span框架推出了Html工具类,可解析h5文本,将其转化为Span对象集合设置到TextView中。
以fromHtml为例,返回SPanned对象,此对象只可读取Span,不可编辑,即无setSpan方法。
跟进,
通过HtmlToSpannedConverer来实现转化,跟进convert
以上,解析h5节点,映射到SpannableStringBuilder对象返回即可,关于怎么解析不赘述。