最近在作需求的時候,常常會有一個文本內,須要單獨高亮其中一部分,而且支持這部分的單獨點擊事件。bash
要實現這樣的功能一般有下面幾種實現方式:app
第一個方案的靈活性不足,這裏不考慮。ide
第二個方案則是使用 HTML 標籤包裹字符串,而後使用 Html.fromHtml(str) 獲得渲染後的字符串,設置給 TextView。這種方法,處理點擊事件比較麻煩,若是想彈出本地的彈窗或者跳轉到另外一個Activity,可能須要經過 JS 注入的方式進行實現。字體
因此最常使用的方案是第三個,SpannableString 或 SpannableStringBuilder 的功能很是強大,可讓一個 TextView 變得豐富多彩,下面來具體看看。ui
SpannableStringBuilder 和 SpannableString 均可以用來顯示富文本,它們的關係就像 StringBuilder 和 String 的關係同樣,SpannableStringBuilder 能夠拼接字符串,SpannableString 不能夠。它們都實現了 Spannable接口。this
Spanspa
SpannableStringBuilder 主要經過 setSpan(Object what, int start, int end, int flags)
這個方法設置不一樣的 Span,改本文本樣式,setSpan 方法能夠屢次調用
。.net
參數解釋:3d
start:開始位置下標 end:結束位置下標,不包含code
what:各類 Span 實例
下面表格中列出部分可用的 Span:
Span | 含義 | 備註 |
---|---|---|
BackgroundColorSpan | 設置文本背景顏色 | 參數傳入一個int類型的顏色值便可 |
ForegroundColorSpan | 設置文本顏色 | 參數傳入一個int類型的顏色值便可 |
ClickableSpan | 設置點擊事件 | 須要繼承這個類重寫onClick方法 |
StrikethroughSpan | 設置刪除線效果 | 無參 |
UnderlineSpan | 設置下劃線效果 | 無參 |
AbsoluteSizeSpan | 設置文字的絕對大小 | 第一個參數爲字體大小,只有這一個參數時,單位爲px,第二個參數dip,默認爲false,設爲true時,第一個參數size的單位是dp |
RelativeSizeSpan | 設置文字的相對大小 | |
StyleSpan | 設置文字粗體、斜體 | Typeface.BOLD爲粗體,Typeface.ITALIC爲斜體, Typeface.BOLD_ITALIC爲粗斜體,做爲參數傳入便可 |
ImageSpan | 設置圖片 | 將[start,end)範圍內的文字替換成參數傳入的圖片 |
MaskFilterSpan | 修飾效果,如模糊(BlurMaskFilter)浮雕 | |
RasterizerSpan | 光柵效果 | |
SuggestionSpan | 至關於佔位符 | |
DynamicDrawableSpan | 設置圖片,基於文本基線或底部對齊 | |
ScaleXSpan | 基於x軸縮放 | |
SubscriptSpan | 下標(數學公式會用到) | |
SuperscriptSpan | 上標(數學公式會用到) | |
TextAppearanceSpan | 文本外貌(包括字體、大小、樣式和顏色) | |
TypefaceSpan | 文本字體 | |
URLSpan | 文本超連接 |
flag:取值有如下四個
flag | 含義 |
---|---|
SPAN_EXCLUSIVE_EXCLUSIVE | 在文本前面或後面插入新的文本時,都不會應用該樣式(前面不包括,後面不包括) |
SPAN_EXCLUSIVE_INCLUSIVE | 在文本前插入新的文本不會應用該樣式,而在文本後插入新文本會應用該樣式(前面不包括,後面包括) |
SPAN_INCLUSIVE_EXCLUSIVE | 在文本前插入新的文本會應用該樣式,而在文本後插入新文本不會應用該樣式(前面包括,後面不包括) |
SPAN_INCLUSIVE_INCLUSIVE | 在文本前面或後面插入新的文本時,都會應用該樣式(前面包括,後面包括) |
EXCLUSIVE:排除的,即不包含 ,INCLUSIVE:包含的。大多使用第一個,先後均不包含。
示例
選擇了其中幾個經常使用 Span 作了個簡單示例,代碼以下:
class SpannableAcitivity : AppCompatActivity() {
val textStr = "夜闌臥聽風吹雨,鐵馬冰河入夢來"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_spannable)
var builder = SpannableStringBuilder().also {
it.append(textStr)
it.setSpan(ForegroundColorSpan(Color.BLUE), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(BackgroundColorSpan(Color.RED), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
Toast.makeText(this@SpannableAcitivity, "聽風吹雨", Toast.LENGTH_SHORT).show()
}
}, 3, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(StrikethroughSpan(), 8, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(UnderlineSpan(), 9, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(AbsoluteSizeSpan(50), 10, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(StyleSpan(Typeface.BOLD), 11, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
it.setSpan(ImageSpan(this@SpannableAcitivity, R.mipmap.ic_launcher),
12, 13, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
spannable_tv.text = builder
spannable_tv.movementMethod = LinkMovementMethod.getInstance()
}
}
複製代碼
效果如圖:
ClickableSpan 注意
在使用 ClickableSpan 時,須要注意如下幾點:
@Override public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(getResources().getColor(R.color.color_black));
ds.setUnderlineText(false);
}
複製代碼
設置 ClickableSpan 後,TextView 須要加一行textView.setMovementMethod(LinkMovementMethod.getInstance());
,不然ClickableSpan部分點擊事件不起做用。
設置 ClickableSpan,可能會與 TextView 自己的onClick事件有衝突。