1 簡介
在使用TextView的時(shí)候,我們經(jīng)常需要在TextView中進(jìn)行圖文混排,比如在QQ中聊天的消息中就會展現(xiàn)表情,比如在微博中,用戶發(fā)出的微博里面經(jīng)常會帶有各種小圖標(biāo)和鏈接积蔚。
Android官方對TextView的圖文混排提供了支持,我們可以從以下三種方式實(shí)現(xiàn)TextView的圖文混排:
- 在TextView的XML布局文件中添加Compound Drawable屬性烦周;
- 在對TextView設(shè)置字符串時(shí)尽爆,可以設(shè)置Html類型的字符串。Html.fromHtml()方法可以對Html的字符串進(jìn)行處理读慎,從而使得Html類型的內(nèi)容滿足TextView的要求漱贱。在給TextView設(shè)置Html類型的內(nèi)容時(shí),還可以傳入一個(gè)ImageGetter夭委,從而對Html類型內(nèi)容中的圖片進(jìn)行處理幅狮;
- 對TextView設(shè)置內(nèi)容的時(shí)候,可以傳入CharSequence類型株灸,而一些CharSequence類型可以利用CharacterStyle進(jìn)行修飾崇摄,從而展現(xiàn)出豐富多彩的內(nèi)容。CharacterStyle擁有很多子類(BackgroundColorSpan慌烧,ClickableSpan逐抑,ImageSpan,TypefaceSpan等)屹蚊,可以產(chǎn)生出各種各樣的效果厕氨。
對于以上三種形式有著不同的使用場景:
- 一般情況下我們希望在字符串的上互妓、下贝攒、左栖茉、右方向添加圖片抄罕,這種需求簡單明確,使用第1種方式就可以了纸型。
- 有時(shí)候我們希望TextView中含有不同顏色的字體,這時(shí)候可以使用第二種方式,只需要在不同顏色的字體上設(shè)置相應(yīng)的顏色即可遭京。第二種方式也可以處理TextView中的鏈接情況,第2中方式還可以在TextView中顯示圖片泞莉。
- 第3種方式可以對TextView中的顯示內(nèi)容進(jìn)行各種變換哪雕,可以對字體背景進(jìn)行設(shè)置,可以對字體顏色進(jìn)行設(shè)置鲫趁,可以在內(nèi)容中加入圖片斯嚎,可以進(jìn)行的操作非常多,但是同時(shí)相應(yīng)的處理也較為復(fù)雜。
下面將會對以上的三種方式分別進(jìn)行講述堡僻,希望能夠讓大家更好地掌握TextView的使用糠惫。
2 Compound Drawable
2.1 一般情況
一般情況下,我們只需要對TextView的上下左右設(shè)置固定的圖片钉疫,這時(shí)候只需要像下面一樣編寫XML文件就可以實(shí)現(xiàn)了硼讽。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/animation"
android:drawableLeft="@drawable/rotating_loading"
android:drawableRight="@drawable/animated_wifi"
android:drawableBottom="@drawable/animated_clock"/>
2.2 圖片動起來
將2.1中的左、右牲阁、下三個(gè)方向的drawable轉(zhuǎn)為動畫drawable固阁,則可以實(shí)現(xiàn)在TextView中顯示動畫的效果。首先我們需要得到TextView四周的drawable城菊,判斷drawable是否實(shí)現(xiàn)了Animatable备燃,如果實(shí)現(xiàn)了則啟動相應(yīng)的動畫效果。
private void startAnimation(TextView textView) {
Drawable[] drawables = textView.getCompoundDrawables();
for (Drawable drawable : drawables) {
if (drawable != null && drawable instanceof Animatable) {
((Animatable) drawable).start();
}
}
}
三個(gè)動畫的drawable:
<!-- res/drawable/rotating_loading.xml -->
<animated-rotate
android:pivotX="50%"
android:pivotY="50%"
android:drawable="@drawable/ic_loading"
android:duration="500" />
<!-- res/drawable/animated_wifi.xml -->
<animation-list>
<item android:drawable="@drawable/ic_wifi_0" android:duration="250" />
<item android:drawable="@drawable/ic_wifi_1" android:duration="250" />
<item android:drawable="@drawable/ic_wifi_2" android:duration="250" />
<item android:drawable="@drawable/ic_wifi_3" android:duration="250" />
</animation-list>
<!-- res/drawable/animated_clock.xml -->
<animated-vector android:drawable="@drawable/clock">
<target android:name="hours" android:animation="@anim/hours_rotation" />
<target android:name="minutes" android:animation="@anim/minutes_rotation" />
</animated-vector>
3 Html Content
3.1 不同字體顏色
一些情況下凌唬,TextView中可能不同的文字有著不同的顏色并齐,這個(gè)時(shí)候處理方式2是非常適用的。
<string name="different_color_text">
<Data><![CDATA[今日已有<font color="#f0717e">1/font>人簽到法瑟,日榜單排在第<font color="#f0717e">1</font>名]]></Data>
</string>
這個(gè)時(shí)候只需要直接對TextView設(shè)置上面的內(nèi)容即可冀膝,展現(xiàn)效果如下所示:
3.2 圖片和鏈接
在一些情況下,TextView中含有圖片和鏈接霎挟,這時(shí)候使用處理方式2也是個(gè)不錯(cuò)的選擇窝剖。
Html代碼:
<h1>Hello World</h1>
Here is an
[站外圖片上傳中……(13)]<i>octopus</i>.<br>
And here is a
<a >link</a>
android字符串:
<string name="from_html_text">
<![CDATA[
<h1>Hello World</h1>
Here is an
[站外圖片上傳中……(14)]<i>octopus</i>.<br>
And here is a
<a >link</a>.
]]>
</string>
給TextView設(shè)置內(nèi)容:
String html = getString(R.string.from_html_text);
/*讓鏈接可點(diǎn)擊*/
textView.setMovementMethod(LinkMovementMethod.getInstance());
/*ResouroceImageGetter用來處理TextView中的圖片*/
textView.setText(Html.fromHtml(html, new ResouroceImageGetter(this), null));
ResouroceImageGetter的作用就是根據(jù)傳過來的src返回drawable,它的代碼如下:
private static class ResouroceImageGetter implements Html.ImageGetter {
// Constructor takes a Context
public Drawable getDrawable(String source) {
int path = context.getResources().getIdentifier(source, "drawable", BuildConfig.APPLICATION_ID);
Drawable drawable = context.getResources().getDrawable(path);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return drawable;
}
}
通過上面的代碼就完成了TextView中鏈接和圖片的設(shè)置酥夭,展示效果如下面所示:
需要注意的是:要讓TextView里面的鏈接生效赐纱,需要對TextView進(jìn)行設(shè)置。
textView.setMovementMethod(LinkMovementMethod.getInstance());
但是上面的代碼會造成當(dāng)TextView設(shè)置最大行數(shù)失敗熬北,當(dāng)超過最大行數(shù)的時(shí)候會造成TextView里面的內(nèi)容可以滑動疙描,在下面的內(nèi)容里面會講解如何解決這個(gè)問題。
4 Span方式
4.1 整體機(jī)理
TextView可以通過下面的方法設(shè)置內(nèi)容讶隐,一般情況下我們會給TextView設(shè)置String類型的內(nèi)容起胰,String類型是實(shí)現(xiàn)了CharSequence接口的。
setText(CharSequence text)
在Google的android官方網(wǎng)站上我們可以得到CharSequence接口的相關(guān)內(nèi)容巫延。
在這里我們需要了解spaned和spanable效五,其實(shí)這兩個(gè)都是接口,而且spanable是繼承spaned炉峰。為了方便理解畏妖,這里先講解spanable接口。
在spanable接口里面定義了下面兩個(gè)抽象方法:
- setSpan(Object what, int start, int end, int flags)疼阔,在這個(gè)方法中what通常指各種類型的span(ImageSpan戒劫、URLSpan半夷、ClickableSpan等),該方法可以將spanable里面從start到end的內(nèi)容替換為指定的span類型的內(nèi)容迅细。其中flags是指設(shè)定start和end的方式巫橄,在下面的內(nèi)容中會講到。
- removeSpan(Object what)茵典,在這個(gè)方法中what也是指各種類型的span嗦随,這個(gè)方法是在spanable中移除特定的span。
關(guān)于上面提到的flags通常使用的是以下4種:
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前后都不包括)敬尺;
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括枚尼,后面不包括);
- Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括砂吞,后面包括)署恍;
- Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括)。
一般來說通常使用的是Spanned.SPAN_INCLUSIVE_EXCLUSIVE蜻直,這是因?yàn)檫@樣我們就可以比較輕松地使用String的length()方法來得到end的位置盯质。
在spaned里面提供了下面5個(gè)抽象方法:
- getSpanEnd(Object tag),這個(gè)方法用來獲取一個(gè)span的結(jié)束位置概而。
- getSpanFlags(Object tag)呼巷,這個(gè)方法用來獲取這個(gè)span設(shè)置的flag。
- getSpanStart(Object tag)赎瑰,這個(gè)方法用來獲取一個(gè)span開始的位置王悍。
- getSpans(int start, int end, Class<T> type),這個(gè)方法用來獲取從start到end的位置上所有的特定類型的span餐曼,比如說我么希望找到某一段里面所有的ClickableSpan就可以使用這個(gè)方法压储。
- nextSpanTransition(int start, int limit, Class type),這個(gè)方法會在你指定的文本范圍內(nèi)源譬,返回下一個(gè)你指定的Span類型的開始位置集惋,依照這個(gè)方法,我們就可以逐層掃描指定的 Span 踩娘,而不用同時(shí)考慮其他類型的Span的影響刮刑,十分有用。
接下來講述的是SpannableString和SpannableStringBuilder兩個(gè)類养渴,這兩個(gè)類實(shí)現(xiàn)了Spannable接口雷绢,實(shí)現(xiàn)了接口里面定義的方法。SpannableString和SpannableStringBuilder的關(guān)系類似于String和StringBuilder的關(guān)系厚脉。SpannableStringBuilder和StringBuilder一樣實(shí)現(xiàn)了Appendable接口习寸,從而可以往里面不斷append內(nèi)容胶惰。在使用Span實(shí)現(xiàn)TextView圖文混排的過程中傻工,一般來說我們都會使用SpannableString和SpannableStringBuilder中的一個(gè)。
所以對于使用Span方式實(shí)現(xiàn)TextView圖文混排的整體流程是:
- 創(chuàng)建一個(gè)SpannableString或者SpannableStringBuilder對象;
- 利用setSpan(Object what, int start, int end, int flags)方法中捆,將SpannableString或者SpannableStringBuilder對象的某些位置的內(nèi)容替換為具體類型的Span鸯匹;
- 利用TextView的setText(CharSequence text)方法將SpannableString或者SpannableStringBuilder對象進(jìn)行展示。
4.2 不同類型的Span
說明:下面的內(nèi)容參考:http://flavienlaurent.com/blog/2014/01/31/spans/
以下是Span的一些規(guī)則:
- 如果一個(gè)Span影響字符級的文本格式泄伪,則繼承CharacterStyle殴蓬;
- 如果一個(gè)Span影響段落層次的文本格式,則實(shí)現(xiàn)ParagraphStyle蟋滴;
- 如果一個(gè)Span修改字符級別的文本外觀染厅,則實(shí)現(xiàn)UpdateAppearance;
- 如果一個(gè)Span修改字符級文本度量|大小津函,則實(shí)現(xiàn)UpdateLayout肖粮。
CharacterStyle:
ParagraphStyle:
UpdateAppearance:
UpdateLayout: