我們可以使用 TextView 做些什么呢?
一般的文本控件可以使用 TextView 來實(shí)現(xiàn),
那么,還有沒有一些其他的場景,可以使用 TextView 實(shí)現(xiàn)呢?
本文想記錄一些利用 TextView 實(shí)現(xiàn)的場景。
涉及到以下內(nèi)容:
- 使用
drawableStart|End實(shí)現(xiàn)文字 + 圖標(biāo) - 使用
Span實(shí)現(xiàn)「文字+圖標(biāo)混排」效果 - 實(shí)現(xiàn)帶背景的文字效果
- 部分點(diǎn)擊事件實(shí)現(xiàn)
- 添加原角背景 + 背景透明度設(shè)置
1. 實(shí)現(xiàn)文字 + 圖標(biāo)實(shí)現(xiàn)
想要實(shí)現(xiàn)類似這樣的效果,文字 + 圖標(biāo)。

其實(shí)實(shí)現(xiàn)起來特別簡單,使用一個(gè) TextView + ImageView 即可。
但是,如果我們可以使用一個(gè) View 搞定,為什么要使用兩個(gè)呢?
在 TextView 中,有個(gè)屬性叫做, drawableXXX, 在 xml 中為:
// 在文字的右邊放置圖片
android:drawableEnd="@drawable/ic_action_like"
// 在文字的左邊放置圖片
android:drawableStart="@drawable/ic_action_like"
// 在文字的上邊放置圖片
android:drawableTop="@drawable/ic_action_like"
// 在文字的下邊放置圖片
android:drawableBottom="@drawable/ic_action_like"
所以,當(dāng)我們想要實(shí)現(xiàn)上述的效果時(shí),可以這么使用:
<TextView
android:id="@+id/like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:drawableEnd="@drawable/ic_action_like"
android:drawablePadding="4dp"
android:gravity="center"
android:paddingBottom="4dp"
android:text="123"
android:textColor="#0084FF"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
上述代碼即可實(shí)現(xiàn),文字 + 圖標(biāo)效果 。
android:drawablePadding是設(shè)置該圖標(biāo)與文字的padding
2. 對 TextView 設(shè)置 Span 實(shí)現(xiàn)「文字+圖標(biāo)混排」效果
上述的實(shí)現(xiàn),只能把圖標(biāo)固定的放在文字的「前、后、上、下」 這四種效果。
對于復(fù)雜的文字圖標(biāo)混排,就無能為力了。
那么如何實(shí)現(xiàn)「文字+圖標(biāo)混排」效果呢?
我們可以使用 ImageSpan 實(shí)現(xiàn)該效果。
例如,我們想要這樣的效果:

在 哈哈哈123,你說不說,那我開始 balabala 了,嘿嘿嘿 中添加愛心圖標(biāo),
實(shí)現(xiàn)代碼如下:
contentView.text = "哈哈哈123,你說不說,那我開始 balabala 了,嘿嘿嘿"
val stringBuilder = SpannableString(contentView.text)
val drawable = getDrawable(R.drawable.ic_action_like)
val imageSpan = ImageSpan(drawable)
// 設(shè)置 drawable 大小,這里簡單處理下
drawable!!.setBounds(0, 0, 64, 64)
stringBuilder.setSpan(imageSpan, 10, 12, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
contentView.text = stringBuilder
常見的消息中的小表情,就是利用 ImageSpan 實(shí)現(xiàn)的
小表情的實(shí)現(xiàn),通常是利用特定的字符標(biāo)志,例如
[笑臉],當(dāng)檢測到有類似這種格式[],會(huì)通過一套解析工具,把[笑臉]替換成一個(gè)ImageSpan, 從而實(shí)現(xiàn)小表情的效果。
3. 實(shí)現(xiàn)帶背景的文字效果
我們想要實(shí)現(xiàn)的效果如下:

TextView.getText() 的返回值是 CharSequence
而 SpannableStringBuilder 實(shí)現(xiàn)了 CharSequence 接口 ,
表明 TextView 是可以設(shè)置 SpannableStringBuilder的,而我們可以利用 SpannableStringBuilder 設(shè)置 span 樣式,完成很多不一樣的樣式實(shí)現(xiàn)。
3.1 給部分字體添加背景
實(shí)現(xiàn)代碼:
private fun setupLikeTextView() {
likeTextView.text = "你說是不是???是不是這樣的,你猜一下,猜不到吧?在猜一下,哈哈哈哈,就不告訴你"
val stringBuilder = SpannableStringBuilder(likeTextView.text)
val backgroundSpan = BackgroundColorSpan(getColor(R.color.colorAccent))
val start = 3
val end = content.length
stringBuilder.setSpan(backgroundSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
likeTextView.text = stringBuilder
}
即可實(shí)現(xiàn),likeTextView.text 從 第 3 個(gè) 字符到結(jié)尾都會(huì)添加一個(gè)帶顏色的背景。
主要代碼是 stringBuilder 的構(gòu)建以及 likeTextView.text 的賦值。
3.2 給部分文字使用不同的顏色
實(shí)現(xiàn)效果如下:

hahhaah 這個(gè)文案部分, 其中的第 3 ~ 4 個(gè)字符使用不同的顏色
我們可以使用 ForegroundColorSpan 來實(shí)現(xiàn)不同顏色的 TextView 文本, 代碼如下:
contentView.text = "hahhaah, 123, 456, 789"
val foregroundColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.colorAccent))
val sb = SpannableStringBuilder(contentView.text)
sb.setSpan(foregroundColorSpan, 3, 5, SpannableStringBuilder.SPAN_INCLUSIVE_EXCLUSIVE)
contentView.text = sb
在 Android 系統(tǒng)中,還有 StrikethroughSpan, UnderlineSpan 等很多可以實(shí)現(xiàn)特定樣式的 Span 可以供我們使用。
4. 給 TextView 設(shè)置部分文本可點(diǎn)擊
通常在 TextView 中,如果有鏈接,我們需要實(shí)現(xiàn)點(diǎn)擊該部分文案時(shí)會(huì)跳轉(zhuǎn)到該鏈接的效果。
代碼實(shí)現(xiàn):
contentView.movementMethod = LinkMovementMethod.getInstance()
contentView.text = "哈哈哈123,后面是一個(gè)鏈接,可部分點(diǎn)擊: http://www.itdecent.cn/u/9d38eab6ce45"
contentView.setLinkTextColor(context.resources.getColor(R.color.blue))
val contentString = contentView.text
val start = contentString.indexOfFirst { it == ':' }
val end = contentView.text.length
val stringBuilder = SpannableString(contentView.text)
val clickableSpan = TLClickableSpan()
stringBuilder.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
contentView.text = stringBuilder
其中 TLClickableSpan 簡單的繼承了 ClickableSpan:
class TLClickableSpan : ClickableSpan() {
override fun onClick(widget: View) {
Log.i("zc_test", "TLClickableSpan is onClick and view is $widget")
}
}
實(shí)現(xiàn)的效果為:

點(diǎn)擊后的效果, log 提示為 :
2020-05-29 16:50:07.136 5602-5602/com.chendroid.learning I/zc_test: TLClickableSpan is onClick and view is com.google.android.material.textview.MaterialTextView{5773c57 VFED..CL. ...P.... 72,117-1096,223 #7f0800b7 app:id/item_content}
注意事項(xiàng)
代碼中:contentView.movementMethod = LinkMovementMethod.getInstance() 是必須的, 如果不設(shè)置不會(huì)響應(yīng)
ClickableSpan的點(diǎn)擊。
同時(shí),1. 當(dāng)ClickableSpan生效時(shí),會(huì)觸發(fā)TextView的監(jiān)聽事件,消費(fèi)該事件;
- 當(dāng)點(diǎn)擊
TextView非點(diǎn)擊部分時(shí),TextView也會(huì)消費(fèi)該事件,會(huì)導(dǎo)致本來應(yīng)該傳遞給ViewGroup的事件被TextView攔截。
為了解決 當(dāng)點(diǎn)擊 TextView 非點(diǎn)擊部分時(shí), TextView 也會(huì)消費(fèi)該事件, 這個(gè)問題,我們可以設(shè)置:
contentView.movementMethod = LinkMovementMethod.getInstance()
// 不可點(diǎn)擊
contentView.isClickable = false
// 不可長按
contentView.isLongClickable = false
// 不聚焦
contentView.isFocusable = false
5. 添加原角背景 + 背景透明度設(shè)置
如果我們想要實(shí)現(xiàn)類似這樣的效果:

實(shí)現(xiàn)圖中自帶透明度和圓角的「
默認(rèn)」圖標(biāo)。
代碼如下:
//xml 中的代碼
<TextView
android:id="@+id/todo_type"
android:layout_width="64dp"
android:layout_height="30dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_blue_8"
android:gravity="center"
android:text="默認(rèn)"
android:textColor="#0084FF"
android:textSize="15sp"
/>
其中 @drawable/bg_blue_8 為圓角背景, 源碼如下:
// bg_blue_8.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp" />
<solid android:color="#0084FF" />
</shape>
那么如何實(shí)現(xiàn)背景的透明度呢?
我們不能直接對 TextView 直接設(shè)置 TextView.setAlpha(xxx). 因?yàn)檫@樣會(huì)影響到整個(gè) TextView 的透明度,也會(huì)給字體添加上透明度。
TextView 中有單獨(dú)對 background 設(shè)置的方法,我們需要這樣設(shè)置:
type = view.todo_type_1
type.apply {
background.alpha = (0.08 * 255).toInt()
...
}
上述代碼即可實(shí)現(xiàn)我們想要的效果。
注:該
UI設(shè)計(jì)實(shí)現(xiàn)方案有很多種,這里只討論如何只用一個(gè)TextView實(shí)現(xiàn)。
這樣實(shí)現(xiàn)的目的:盡可能使界面的渲染高效。避免不必要的內(nèi)存浪費(fèi)。
6. 總結(jié)
記錄了一下,日常在代碼中利用 TextView 實(shí)現(xiàn)的一些效果,
內(nèi)容不難,簡單的介紹了代碼實(shí)現(xiàn)方式。
我們還可以利用 TextView 實(shí)現(xiàn)很多樣式的混排效果, 使用 Html 解析或者設(shè)置 Span
可以解決我們遇到的大部分問題。
這篇文章,如有錯(cuò)誤,還請見諒,指出。
2020.5.30 by chendroid