前言
最近一直在做 TextView 富文字處理的工作,接觸到了比較纖細的粗
、字型顏色
、內聯圖片
、處理 ellipsize
、LinkMovementMethod 的坑
和ClickableSpan 的點選事件
等問題,經過查資料
、看原始碼
和做實驗
三連之後,對 Android TextView 的富文字處理有了小小的體會,寫篇部落格總結下自己的收穫,日後在遇到類似問題也方便回顧。
android.text.style
富文字相關的 xxxSpan
基本都在這個包下面啦,android-28
裡的有 42 個檔案,包含了各式各樣設定字型
、自定義繪製
、點選事件
、段落樣式
的 Span。
1、CharacterStyle(來來來,你來挑畫筆)
多數 Span 的基類,有一個抽象方法:
public abstract void updateDrawState(TextPaint tp);
TextPaint 繼承自 Paint,這就給了我們一個機會去操縱畫筆,設定前景色
、背景色
、字型顏色
、字型粗細
、字型大小
等等等等,想怎麼設定怎麼設定。
設計:這裡的使用者名稱要單獨加粗
我:好,加粗
設計:不行,這樣太粗了,要那種比較纖細的粗
我:我 。。。
複製程式碼
Paint.setFakeBoldText
就適用於這種情況,這個方法其實就是把畫筆變粗了而已,而不是使用粗體的字型,要更精細地控制畫筆粗細可以用Paint.setStrokeWidth
。
附上解決問題連結:
如何實現 “中間這幾個字要加粗,但是不要太粗,比較纖細的那種粗” ?
2、UpdateAppearance
這個介面是空的,只是用來標識的,表示 Span 會改變字型的外貌(顏色、形狀等),UpdateLayout
稍後說,其他的 Span 都實現 CharacterStyle
的 updateDrawState
方法,並對畫筆進行設定,達到變換字型外觀的目的。
3、ClickableSpan
增加了一個抽象方法public abstract void onClick(@NonNull View widget);
,用於響應點選事件(需要外部配合,如 LinkMovementMethod
或OnTouchListener
)。
URLSpan
繼承自它,儲存一個 URL,點選時用 startActivity
開啟。
4、UpdateLayout
跟UpdateAppearance
一樣是一個標識,從名字就能看出來,它還改變佈局(位置、大小等),
唯一的繼承類 MetricAffectingSpan
有一個抽象方法
public abstract void updateMeasureState(@NonNull TextPaint textPaint);
,用於改變字型的大小等屬性。
5、ReplacementSpan (畫布給你,你行你來畫)
之前的 Span 我們只能設定Paint
,能做的事情很有限, ReplacementSpan
比MetricAffectingSpan
更進一步,直接自己接管了繪製的任務,它有兩個抽象方法public abstract int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm);
和public abstract void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint);
分別用於確定繪製範圍和繪製內容。這就使得ReplacementSpan
有很大的靈活性,想畫啥就畫啥,經常用於畫內聯的圖片之類的東西。
6、其他的 Span
ParagraphStyle
相關的也有很多 Span,主要用於段落樣式的處理,我也沒用過,就不瞎說了
TtsSpan
、SuggestionSpan
、EasyEditSpan
、SpellCheckSpan
、SuggestionRangeSpan
。。。
最後
Android 提供了這麼多 Span,我平時用的比較多的也就ReplacementSpan
和ClickableSpan
,ReplacementSpan
用於現實自定義的 emoji 表情和內聯的一些小圖示之類的,ClickableSpan
則用於自定義點選事件,比如在點選@Somebody
、#話題#
、檢視圖片
等操作時,用本地的頁面開啟。這兩個 Span 用的比較多,坑也比較多,接下來會專門對這兩個 Span 做個稍微詳細的總結。
Html.fromHtml
實現的富文字處理其實也是利用 Android 提供的 Span 實現,只不過 TextView 支援的標籤十分有限,做一些簡單的處理還行,畢竟 TextView 主要顯示文字嘛,幹嘛整那麼複雜。我平時用的時候也主要是用超連結
,然後通過自定義的ClickableSpan
和Route
開啟本地頁面。