Android圖文混排實現方式詳解

Ruheng發表於2017-08-31

在使用TextView的時候,我們經常需要在TextView中進行圖文混排,比如在QQ中聊天的訊息中的表情,底部tab圖示等。

一、場景

二、實現方式

Android官方對TextView的圖文混排提供了支援,我們可以從以下三種方式實現TextView的圖文混排:

1.在TextView中使用Compound Drawable屬性;
2.在TextView中使用Spannable多樣式顯示;
3.在TextView中顯示HTML文字。

三、drawable屬性

在TextView中使用Compound Drawable屬性可以在文字的上下左右放置drawable,效果如下:


一共有兩種方式可以實現:XML佈局設定和Java程式碼設定。

1. xml佈局

 <TextView
        android:gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="檢視新的內容"
        android:textSize="18sp"
        android:drawablePadding="10dp"
        android:drawableLeft="@drawable/arrow"/>複製程式碼
android:drawableLeft 在文字左邊設定圖片
android:drawableTop 在文字上邊設定圖片
android:drawableRight 在文字右邊設定圖片
android:drawableBottom 在文字下邊設定圖片
android:drawableStart API 17後生效,LTR時在左邊,RTL時在右邊
android:drawableEnd API 17後生效,LTR時在右邊,RTL時在左邊
android:drawablePadding 圖片和文字的間距

2. java程式碼

        textView = (TextView) findViewById(R.id.textView);
        Drawable drawable = getResources().getDrawable(R.drawable.see);
        drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());
        textView.setCompoundDrawables(null,null,null,drawable);
        textView.setCompoundDrawablePadding(10);複製程式碼

注意:必須setBounds()測量圖片邊界,否則不顯示。

setCompoundDrawables 設定上下左右位置的圖片
setCompoundDrawablesRelative 設定四周的圖片,其中兩邊對應xml中的start,end
setCompoundDrawablesWithIntrinsicBounds 設定上下左右位置的圖片,圖片有預設的邊界
setCompoundDrawablesRelativeWithIntrinsicBounds 設定四周的圖片,圖片有預設的邊界
setCompoundDrawablePadding 設定圖片與文字之間的間距

3.缺陷

當TextView設定成固定大小時,由於文字距離邊界的距離過大,會導致文字與圖片之間設定的間距無效,如下圖。

解決方案:

①設定TextView的內填充

通過設定paddingLeft、paddingRight、paddingTop、paddingBottom來縮寫這個間距

②自定義TextView重新佈局

a.先自定義屬性iconPadding來設定間距,並提供方法給外部呼叫。

b.重寫setCompoundDrawablesWithIntrinsicBounds()方法來獲取我們設定的drawable寬高。

c.最後重寫onLayout方法。

可以先參考:Android技巧之drawablePadding的那些事,該篇文章只解決了左右失效的問題。後期會整理個解決圖文混排的工具庫,裡面會有具體方案。

四、Spannable使用

1.簡介

setText(CharSequence text)中接收的是CharSequence。而SpannableString和SpannableStringBuilder是其實現類,是可以直接賦值的。並且兩者的setSpan()方法可以設定一些格式物件(例如字型大小、下劃線、替換為圖片等),這就可以實現富文字了。

Spannable實現子類:SpannableString,SpannableStringBuilder(可變,類似於StringBuilder)。

Spannable中定義了抽象方法:setSpan(Object what, int start, int end, int flags)和removeSpan(Object what)。這兩個方法實現了對字串的靈活編輯。

其中setSpan()方法包含如下引數:

引數 引數說明
what span樣式
start 樣式開始的索引
end 樣式結束的索引
flags 樣式作用的範圍

flags常用的有四種

SPAN_INCLUSIVE_INCLUSIVE 前後都包括,在指定範圍前後插入新字元,都會應用新樣式
SPAN_EXCLUSIVE_EXCLUSIVE 前後都不包括,在指定範圍前後插入新字元,兩端樣式無變化
SPAN_INCLUSIVE_EXCLUSIVE 前面包括,後面不包括
SPAN_EXCLUSIVE_INCLUSIVE 後面包括,前面不包括

通常在insert方式才生效,平時不生效,具體看:Explain the meaning of Span flags like SPAN_EXCLUSIVE_EXCLUSIVE

2.常用span類

常用類 說明
BackgroundColorSpan 背景色樣式,顯然可以用來設定文字的背景色
ForegroundColorSpan 字型顏色樣式,用於改變字型顏色
StyleSpan 主要由正常、粗體、斜體和同時加粗傾斜四種樣式
TypefaceSpan 設定不同的字型
ImageSpan 圖片樣式,主要用於在文字中插入圖片
URLSpan 可以開啟一個連結
UnderlineSpan 下劃線樣式
StrikethroughSpan 刪除線樣式

3.使用方式

        SpannableString span3 = new SpannableString("我如果愛你");
        ImageSpan image = new ImageSpan(this,R.drawable.collect, DynamicDrawableSpan.ALIGN_BOTTOM);
        span3.setSpan(image,3,4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv3.setText(span3);複製程式碼

其中ImageSpan預設對其方式有兩種:ALIGN_BOTTOM及ALIGN_BASELINE。很可惜我們平常用的居中對其的方式沒有,不過可以通過自定義實現,後續會在開源出來。

4.效果

五、HTML顯示

一般顯示HTML內容有兩種方式:

  • 使用 Android 提供的 WebView 控制元件。
  • 通過將 HTML 內容轉化為 Spanned 格式在 TextView 中進行顯示。

現在大多數都用WebView的方式。但是並不是所有的場景下都適合使用 WebView 來顯示 HTML 內容,例如,如果應用要顯示的內容只是一部分 HTML 片段,就可以利用 TextView 來進行顯示,並且效率較高。

由於這種方式不太常用,就不深入介紹,裡面可以實現的效果還是很好的。

1.簡介

Android 中的 TextView 元件常用於顯示文字內容,其實它也可以顯示 HTML 的內容。

簡單來講,這就需要先把 HTML 的內容以字串的形式獲取後,經過 android.text.Html.fromHtml()轉化成 Spanned 的格式,然後將其傳遞到 TextView 的 setText()方法中,這樣就可以在 TextView 中顯示 HTML 頁面的內容了。

需要注意的是,並不是所有的 HTML 標籤在 TextView 中都是支援的,且官方文件並沒有明確的說明支援 HTML 標籤列表,通過檢視 Android 原始碼,可以得到簡單的支援列表。

<br>,< p>,< div align=>,< strong>, <b>, <em>, <cite>, <dfn>, <i>, <big>, <small>, <font size=>, <font color=>, <blockquote>, <tt>, <a href=>,
<u>, <sup>, <sub>, <h1>,<h2>,<h3>,<h4>,<h5>,<h6>, <img src=>, <strike>複製程式碼

下面的示例來介紹如何在 TextView 中顯示一段 HTML 內容,要顯示的這段 HTML 內容即包含超連結內容,也包含有圖片。

2.使用

fromHtml方法

fromHtml(String source, ImageGetter imageGetter,TagHandler tagHandler)複製程式碼
  • source,就是包含 HTML 內容的字串。Html.ImageGetter 和 Html.TagHandler 是兩個介面,提供給開發者繼承使用。
  • imageGetter, 如果要顯示圖片是需要被繼承的,重寫 getDrawable(String source)方法,用於獲取 HTML 裡面的圖片來顯示在 TextView 中。
  • tagHandler,其作用是把 HTML 帶標記的文字內容字串轉化成可以顯示效果的的 Spanned 字串 。由於並非所有的 HTML 標籤都可以轉化,所以在使用時,使用者需要自己新增一些必要的標籤和處理方法時才會繼承使用的。

繼承ImageGetter

繼承於 ImageGetter,重寫 getDrawable (String source) 方法。通過非同步操作,讀取本地/網路資源,獲得drawable物件。

繼承TagHandler

繼承於 TagHandler,重寫了 handleTag()方法。為了支援更多的標籤,例如為了支援<ul><ol><dd><li>標籤,這四個標籤是在 formHtml()方法中本身是不支援。

如果開發者認為安卓 TagHandler 提供的預設標籤解析已經夠用,直接在 fromHtml()方法中第三個引數的地方填寫 null 既可。

最後,通過 formHtml()方法將 HTML 內容轉化為可供顯示的 SpannableString,將 SpannableString 通過 setText 方法放入 TextView 中,就可以顯示圖文並茂的內容了。

使用者互動

formHtml()方法已經將 HTML 內容中的超連結和圖片轉義成為 UrlSpan 和 ImageSpan,進而在 TextView 中完成顯示。但是此時是沒有任何使用者互動的,使用者只能看到 HTML 的內容,下面介紹如何新增使用者互動功能。

要完成使用者互動,這裡我們需要在 TextView 中還需要呼叫textView.setMovementMethod()方法。

Android 提供了 LinkMovementMethod 類以實現了對於文字內容中超連結的遍歷,並且支援對於超連結的點選事件。

所以只要在新增下面一行程式碼,就可以使點選 UrlSpan 能夠觸發開啟連結的功能。

textView.setMovementMethod(LinkMovementMethod.getInstance())複製程式碼

如果想要更多的使用者互動效果,可以自定義LinkMovementMethod 類,重寫onTouchEvent方法來實現。

3.效果


點選圖片,跳轉到圖片詳情頁。

關於HTML顯示這部分,沒做具體實現。具體可以看:靈活高效的在 Android Native App 開發中顯示 HTML 內容,裡面有具體原始碼可以下載,HTML部分內容也是參考該篇文章完成的。
開源庫:html-textview

六、總結

以上就是關於圖文混排的一些解決方案,相信通過這些瞭解,對於工作中的實際場景的使用大家會有適當的解決方案。由於實際應用較少,所以認識較為淺顯,可能有些地方描述不當,後期會考慮封裝個解決圖文混排的工具類,加深下理解。

相關文章