TextView圖文混排基礎(chǔ)

1 簡介

在使用TextView的時(shí)候,我們經(jīng)常需要在TextView中進(jìn)行圖文混排,比如在QQ中聊天的消息中就會展現(xiàn)表情,比如在微博中,用戶發(fā)出的微博里面經(jīng)常會帶有各種小圖標(biāo)和鏈接积蔚。
Android官方對TextView的圖文混排提供了支持,我們可以從以下三種方式實(shí)現(xiàn)TextView的圖文混排:

  1. 在TextView的XML布局文件中添加Compound Drawable屬性烦周;
  2. 在對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)行處理幅狮;
  3. 對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"/>
drawable+方向

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>
drawable+方向+動畫

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)效果如下所示:


不同顏色文字內(nèi)容

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)容巫延。


CharSequence

CharSequence方法

在這里我們需要了解spaned和spanable效五,其實(shí)這兩個(gè)都是接口,而且spanable是繼承spaned炉峰。為了方便理解畏妖,這里先講解spanable接口。
在spanable接口里面定義了下面兩個(gè)抽象方法:


Spanable方法
  • 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種:

  1. Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前后都不包括)敬尺;
  2. Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括枚尼,后面不包括);
  3. Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括砂吞,后面包括)署恍;
  4. Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括)。

一般來說通常使用的是Spanned.SPAN_INCLUSIVE_EXCLUSIVE蜻直,這是因?yàn)檫@樣我們就可以比較輕松地使用String的length()方法來得到end的位置盯质。
在spaned里面提供了下面5個(gè)抽象方法:


Spaned方法
  • 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圖文混排的整體流程是:

  1. 創(chuàng)建一個(gè)SpannableString或者SpannableStringBuilder對象;
  2. 利用setSpan(Object what, int start, int end, int flags)方法中捆,將SpannableString或者SpannableStringBuilder對象的某些位置的內(nèi)容替換為具體類型的Span鸯匹;
  3. 利用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:


CharacterStyle

ParagraphStyle:


ParagraphStyle

UpdateAppearance:
UpdateAppearance

UpdateLayout:


UpdateLayout

5 相關(guān)鏈接

Textview圖文基礎(chǔ)
段落級span
字符級span
自定義span

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尔苦,隨后出現(xiàn)的幾起案子涩馆,更是在濱河造成了極大的恐慌,老刑警劉巖允坚,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魂那,死亡現(xiàn)場離奇詭異,居然都是意外死亡稠项,警方通過查閱死者的電腦和手機(jī)涯雅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來展运,“玉大人斩芭,你說我怎么就攤上這事±纸” “怎么了划乖?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挤土。 經(jīng)常有香客問我琴庵,道長,這世上最難降的妖魔是什么仰美? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任迷殿,我火速辦了婚禮,結(jié)果婚禮上咖杂,老公的妹妹穿的比我還像新娘庆寺。我一直安慰自己,他們只是感情好诉字,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布懦尝。 她就那樣靜靜地躺著知纷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陵霉。 梳的紋絲不亂的頭發(fā)上琅轧,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音踊挠,去河邊找鬼乍桂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛效床,可吹牛的內(nèi)容都是我干的睹酌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼剩檀,長吁一口氣:“原來是場噩夢啊……” “哼忍疾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谨朝,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤卤妒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后字币,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體则披,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年洗出,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了士复。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翩活,死狀恐怖阱洪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情菠镇,我是刑警寧澤冗荸,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站利耍,受9級特大地震影響蚌本,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隘梨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一程癌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轴猎,春花似錦嵌莉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽中鼠。三九已至,卻和暖如春只祠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扰肌。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工抛寝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人曙旭。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓盗舰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桂躏。 傳聞我的和親對象是個(gè)殘疾皇子钻趋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容