一,概述
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對象返回即可,關于怎么解析不贅述。