TextView圖文混排基礎

轉自:http://www.jianshu.com/p/72d31b7da85b

1 簡介

在使用TextView的時候,我們經常需要在TextView中進行圖文混排,比如在QQ中聊天的消息中就會展現表情,比如在微博中,用戶發出的微博裏面經常會帶有各種小圖標和鏈接。
Android官方對TextView的圖文混排提供了支持,我們可以從以下三種方式實現TextView的圖文混排:

  1. 在TextView的XML佈局文件中添加Compound Drawable屬性;
  2. 在對TextView設置字符串時,可以設置Html類型的字符串。Html.fromHtml()方法可以對Html的字符串進行處理,從而使得Html類型的內容滿足TextView的要求。在給TextView設置Html類型的內容時,還可以傳入一個ImageGetter,從而對Html類型內容中的圖片進行處理;
  3. 對TextView設置內容的時候,可以傳入CharSequence類型,而一些CharSequence類型可以利用CharacterStyle進行修飾,從而展現出豐富多彩的內容。CharacterStyle擁有很多子類(BackgroundColorSpan,ClickableSpan,ImageSpan,TypefaceSpan等),可以產生出各種各樣的效果。

對於以上三種形式有着不同的使用場景:

  • 一般情況下我們希望在字符串的上、下、左、右方向添加圖片,這種需求簡單明確,使用第1種方式就可以了。
  • 有時候我們希望TextView中含有不同顏色的字體,這時候可以使用第二種方式,只需要在不同顏色的字體上設置相應的顏色即可。第二種方式也可以處理TextView中的鏈接情況,第2中方式還可以在TextView中顯示圖片。
  • 第3種方式可以對TextView中的顯示內容進行各種變換,可以對字體背景進行設置,可以對字體顏色進行設置,可以在內容中加入圖片,可以進行的操作非常多,但是同時相應的處理也較爲複雜。

下面將會對以上的三種方式分別進行講述,希望能夠讓大家更好地掌握TextView的使用。


2 Compound Drawable

2.1 一般情況

一般情況下,我們只需要對TextView的上下左右設置固定的圖片,這時候只需要像下面一樣編寫XML文件就可以實現了。

<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中的左、右、下三個方向的drawable轉爲動畫drawable,則可以實現在TextView中顯示動畫的效果。首先我們需要得到TextView四周的drawable,判斷drawable是否實現了Animatable,如果實現了則啓動相應的動畫效果。

private void startAnimation(TextView textView) {
  Drawable[] drawables = textView.getCompoundDrawables();
  for (Drawable drawable : drawables) {
    if (drawable != null && drawable instanceof Animatable) {
      ((Animatable) drawable).start();
    }
  }
}

三個動畫的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中可能不同的文字有着不同的顏色,這個時候處理方式2是非常適用的。

<string name="different_color_text">
<Data><![CDATA[今日已有<font color="#f0717e">1/font>人簽到,日榜單排在第<font color="#f0717e">1</font>名]]></Data>
</string>

這個時候只需要直接對TextView設置上面的內容即可,展現效果如下所示:


不同顏色文字內容

3.2 圖片和鏈接

在一些情況下,TextView中含有圖片和鏈接,這時候使用處理方式2也是個不錯的選擇。
Html代碼:

<h1>Hello World</h1>
Here is an
[站外圖片上傳中……(13)]<i>octopus</i>.<br>
And here is a
<a href="http://d.android.com">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 href="http://d.android.com">link</a>.
]]>
</string>

給TextView設置內容:

String html = getString(R.string.from_html_text);
/*讓鏈接可點擊*/
textView.setMovementMethod(LinkMovementMethod.getInstance());
/*ResouroceImageGetter用來處理TextView中的圖片*/
textView.setText(Html.fromHtml(html, new ResouroceImageGetter(this), null));

ResouroceImageGetter的作用就是根據傳過來的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中鏈接和圖片的設置,展示效果如下面所示:


帶有圖片和鏈接


需要注意的是:要讓TextView裏面的鏈接生效,需要對TextView進行設置。

textView.setMovementMethod(LinkMovementMethod.getInstance());

但是上面的代碼會造成當TextView設置最大行數失敗,當超過最大行數的時候會造成TextView裏面的內容可以滑動,在下面的內容裏面會講解如何解決這個問題。


4 Span方式

4.1 整體機理

TextView可以通過下面的方法設置內容,一般情況下我們會給TextView設置String類型的內容,String類型是實現了CharSequence接口的。

setText(CharSequence text)

在Google的android官方網站上我們可以得到CharSequence接口的相關內容。


CharSequence

CharSequence方法


在這裏我們需要了解spaned和spanable,其實這兩個都是接口,而且spanable是繼承spaned。爲了方便理解,這裏先講解spanable接口。
在spanable接口裏面定義了下面兩個抽象方法:


Spanable方法
  • setSpan(Object what, int start, int end, int flags),在這個方法中what通常指各種類型的span(ImageSpan、URLSpan、ClickableSpan等),該方法可以將spanable裏面從start到end的內容替換爲指定的span類型的內容。其中flags是指設定start和end的方式,在下面的內容中會講到。
  • removeSpan(Object what),在這個方法中what也是指各種類型的span,這個方法是在spanable中移除特定的span。

關於上面提到的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,這是因爲這樣我們就可以比較輕鬆地使用String的length()方法來得到end的位置。
在spaned裏面提供了下面5個抽象方法:


Spaned方法
  • getSpanEnd(Object tag),這個方法用來獲取一個span的結束位置。
  • getSpanFlags(Object tag),這個方法用來獲取這個span設置的flag。
  • getSpanStart(Object tag),這個方法用來獲取一個span開始的位置。
  • getSpans(int start, int end, Class<T> type),這個方法用來獲取從start到end的位置上所有的特定類型的span,比如說我麼希望找到某一段裏面所有的ClickableSpan就可以使用這個方法。
  • nextSpanTransition(int start, int limit, Class type),這個方法會在你指定的文本範圍內,返回下一個你指定的Span類型的開始位置,依照這個方法,我們就可以逐層掃描指定的 Span ,而不用同時考慮其他類型的Span的影響,十分有用。

接下來講述的是SpannableString和SpannableStringBuilder兩個類,這兩個類實現了Spannable接口,實現了接口裏面定義的方法。SpannableString和SpannableStringBuilder的關係類似於String和StringBuilder的關係。SpannableStringBuilder和StringBuilder一樣實現了Appendable接口,從而可以往裏面不斷append內容。在使用Span實現TextView圖文混排的過程中,一般來說我們都會使用SpannableString和SpannableStringBuilder中的一個。
所以對於使用Span方式實現TextView圖文混排的整體流程是:

  1. 創建一個SpannableString或者SpannableStringBuilder對象;
  2. 利用setSpan(Object what, int start, int end, int flags)方法,將SpannableString或者SpannableStringBuilder對象的某些位置的內容替換爲具體類型的Span;
  3. 利用TextView的setText(CharSequence text)方法將SpannableString或者SpannableStringBuilder對象進行展示。

4.2 不同類型的Span

說明:下面的內容參考:http://flavienlaurent.com/blog/2014/01/31/spans/
以下是Span的一些規則:

  • 如果一個Span影響字符級的文本格式,則繼承CharacterStyle;
  • 如果一個Span影響段落層次的文本格式,則實現ParagraphStyle;
  • 如果一個Span修改字符級別的文本外觀,則實現UpdateAppearance;
  • 如果一個Span修改字符級文本度量|大小,則實現UpdateLayout。

CharacterStyle:


CharacterStyle


ParagraphStyle:


ParagraphStyle


UpdateAppearance:


UpdateAppearance


UpdateLayout:


UpdateLayout
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章