ndroid 中的 TextView 組件常用于顯示文本內(nèi)容,其實(shí)它也可以顯示 HTML 的內(nèi)容。簡(jiǎn)單來講,這就需要先把 HTML 的內(nèi)容以字符串的形式獲取后,經(jīng)過 android.text.Html.fromHtml()轉(zhuǎn)化成 Spanned 的格式,然后將其傳遞到 TextView 的 setText()方法中,這樣就可以在 TextView 中顯示 HTML 頁面的內(nèi)容了。需要注意的是,并不是所有的 HTML 標(biāo)簽在 TextView 中都是支持的,且官方文檔并沒有明確的說明支持 HTML 標(biāo)簽列表,通過查看 Android 源代碼,可以得到簡(jiǎn)單的支持列表。
{<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>}
1
下面的示例來介紹如何在 TextView 中顯示一段 HTML 內(nèi)容,要顯示的這段 HTML 內(nèi)容即包含超鏈接內(nèi)容,也包含有圖片。
在 TextView 中顯示 HTML 內(nèi)容
顯示的過程中最主要的過程就是調(diào)用 Android.text.Html 類提供的 fromHtml()方法,將一段 HTML 內(nèi)容轉(zhuǎn)化為 Spanned 對(duì)象。
Android.text.Html 類提供的 fromHtml()方法使用如下清單 4
fromHtml()方法定義
public static Spanned fromHtml(String source, ImageGetter imageGetter,
TagHandler tagHandler) {
……
HtmlToSpannedConverter converter =
new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser);
return converter.convert();
}
1234567
source,就是包含 HTML 內(nèi)容的字符串。而 Html.ImageGetter 和 Html.TagHandler 是兩個(gè)接口,提供給開發(fā)者繼承使用。
imageGetter, 如果要顯示圖片是需要被繼承的,重寫 getDrawable(String source)方法,用于獲取 HTML 里面的圖片來顯示在 TextView 中。
tagHandler,其作用是把 HTML 帶標(biāo)記的文本內(nèi)容字符串轉(zhuǎn)化成可以顯示效果的的 Spanned 字符串 。由于并非所有的 HTML 標(biāo)簽都可以轉(zhuǎn)化,所以在使用時(shí),用戶需要自己添加一些必要的標(biāo)簽和處理方法時(shí)才會(huì)繼承使用的。
在本例中使用 fromHtml()方法之前,要準(zhǔn)備好該方法要用的三個(gè)參數(shù)內(nèi)容,首先將 HTML 字符串內(nèi)容準(zhǔn)備好,在項(xiàng)目中需要?jiǎng)?chuàng)建兩個(gè)類 MImageGetter 和 MTagHandler 分別繼承于 ImageGetter 和 TagHandler,分別用戶圖片的獲取,和特殊標(biāo)簽的支持。
MImageGetter
繼承于 ImageGetter,重寫 getDrawable (String source) 方法中從 assets 路徑下取出的圖片流(這里當(dāng)然也可以通過網(wǎng)絡(luò)操作來完成圖片流的獲取),最后獲得可供顯示的圖片對(duì)象,例如 Drawable 對(duì)像。由于 Android 設(shè)備的異構(gòu)性,為了有更好的顯示效果,通常需要獲取屏幕大小,然后調(diào)用 drawable.setBounds () 還可以重新設(shè)置圖片的大小, 最后返回合適大小的圖片 Drawable 對(duì)象。 由此 Spanned 中的 ImageSpan 就獲得了圖像被顯示在 TextView 中對(duì)應(yīng)位置了。
TypedValue typedValue = new TypedValue();
typedValue.density = TypedValue.DENSITY_DEFAULT;
drawable = Drawable.createFromResourceStream(null, typedValue, is, "src");
DisplayMetrics dm = c.getResources().getDisplayMetrics();
int dwidth = dm.widthPixels-10;//padding left + padding right
float dheight = (float)drawable.getIntrinsicHeight()*(float)dwidth/(float)drawable.getIntrinsicWidth();
int dh = (int)(dheight+0.5);
int wid = dwidth;
int hei = dh;
drawable.setBounds(0, 0, wid, hei);DisplayMetrics dm = c.getResources().getDisplayMetrics();
12345678910
MTagHandler
繼承于 TagHandler,重寫了 handleTag()方法,為的是支持部分標(biāo)簽,這四個(gè)標(biāo)簽是在 formHtml()方法中本身是不支持。如果開發(fā)者認(rèn)為安卓 TagHandler 提供的默認(rèn)標(biāo)簽解析已經(jīng)夠用,直接在 fromHtml()方法中第三個(gè)參數(shù)的地方填寫 null 既可。
重寫 handleTag()方法
public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
if (tag.equals("ul") || tag.equals("ol") || tag.equals("dd")) {
if (opening) {
mListParents.add(tag);
} else mListParents.remove(tag);
} else if (tag.equals("li") && !opening) {
handleListTag(output);
}
}
private void handleListTag(Editable output) {
……
}
123456789101112
最后,在完成了 MImageGetter、MTagHandler 以后,就可以通過 formHtml()方法將 HTML 內(nèi)容轉(zhuǎn)化為可供顯示的 SpannableString,將 SpannableString 通過 setText 方法放入 TextView 中,就可以顯示圖文并茂的內(nèi)容了。
progressBar.setVisibility(View.GONE);
text.setText(Html.fromHtml(htmlCont, new MImageGetter(text,MainActivity.this), new MTagHandler()));
text.setVisibility(View.VISIBLE);
123
MImageGetter、MTagHandler 如下:
者博客
http://www.jianshu.com/u/0fa6f5d09040
文章目錄
前言
場(chǎng)景
實(shí)現(xiàn)方式
drawable屬性
Spannable使用
HTML顯示
總結(jié)
0
前言
在使用TextView的時(shí)候,我們經(jīng)常需要在TextView中進(jìn)行圖文混排,比如在QQ中聊天的消息中的表情,底部tab圖標(biāo)等。
1
場(chǎng)景
2
實(shí)現(xiàn)方式
Android官方對(duì)TextView的圖文混排提供了支持,我們可以從以下三種方式實(shí)現(xiàn)TextView的圖文混排:
1.在TextView中使用Compound Drawable屬性;
2.在TextView中使用Spannable多樣式顯示;
3.在TextView中顯示HTML文本。
3
drawable屬性
在TextView中使用Compound Drawable屬性可以在文字的上下左右放置drawable,效果如下:
一共有兩種方式可以實(shí)現(xiàn):XML布局設(shè)置和Java代碼設(shè)置。
1. xml布局
2. java代碼
注意:必須setBounds測(cè)量圖片邊界,否則不顯示。
3.缺陷
當(dāng)TextView設(shè)置成固定大小時(shí),由于文字距離邊界的距離過大,會(huì)導(dǎo)致文字與圖片之間設(shè)置的間距無效,如下圖。
解決方案:
①設(shè)置TextView的內(nèi)填充
通過設(shè)置paddingLeft、paddingRight、paddingTop、paddingBottom來縮寫這個(gè)間距
②自定義TextView重新布局
a.先自定義屬性iconPadding來設(shè)置間距,并提供方法給外部調(diào)用。
b.重寫setCompoundDrawablesWithIntrinsicBounds方法來獲取我們?cè)O(shè)置的drawable寬高。
c.最后重寫onLayout方法。
可以先參考:Android技巧之drawablePadding的那些事(https://yuxingxin.com/2015/11/05/DrawablePadding/),該篇文章只解決了左右失效的問題。后期會(huì)整理個(gè)解決圖文混排的工具庫,里面會(huì)有具體方案。
4
Spannable使用
1.簡(jiǎn)介
setText(CharSequence text)中接收的是CharSequence。而SpannableString和SpannableStringBuilder是其實(shí)現(xiàn)類,是可以直接賦值的。并且兩者的setSpan方法可以設(shè)置一些格式對(duì)象(例如字體大小、下劃線、替換為圖片等),這就可以實(shí)現(xiàn)富文本了。
Spannable實(shí)現(xiàn)子類:SpannableString,SpannableStringBuilder(可變,類似于StringBuilder)。
Spannable中定義了抽象方法:setSpan(Object what, int start, int end, int flags)和removeSpan(Object what)。這兩個(gè)方法實(shí)現(xiàn)了對(duì)字符串的靈活編輯。
其中setSpan方法包含如下參數(shù):
flags常用的有四種
通常在insert方式才生效,平時(shí)不生效,具體看:Explain the meaning of Span flags like SPAN_EXCLUSIVE_EXCLUSIVE。(https://stackoverflow.com/questions/9879233/explain-the-meaning-of-span-flags-like-span-exclusive-exclusive)
2.常用span類
3.使用方式
其中ImageSpan默認(rèn)對(duì)其方式有兩種:ALIGN_BOTTOM及ALIGN_BASELINE。很可惜我們平常用的居中對(duì)其的方式?jīng)]有,不過可以通過自定義實(shí)現(xiàn),后續(xù)會(huì)在開源出來。
4.效果
5
HTML顯示
一般顯示HTML內(nèi)容有兩種方式:
使用 Android 提供的 WebView 控件。
通過將 HTML 內(nèi)容轉(zhuǎn)化為 Spanned 格式在 TextView 中進(jìn)行顯示。
現(xiàn)在大多數(shù)都用WebView的方式。但是并不是所有的場(chǎng)景下都適合使用 WebView 來顯示 HTML 內(nèi)容,例如,如果應(yīng)用要顯示的內(nèi)容只是一部分 HTML 片段,就可以利用 TextView 來進(jìn)行顯示,并且效率較高。
由于這種方式不太常用,就不深入介紹,里面可以實(shí)現(xiàn)的效果還是很好的。
1.簡(jiǎn)介
Android 中的 TextView 組件常用于顯示文本內(nèi)容,其實(shí)它也可以顯示 HTML 的內(nèi)容。
簡(jiǎn)單來講,這就需要先把 HTML 的內(nèi)容以字符串的形式獲取后,經(jīng)過 android.text.Html.fromHtml轉(zhuǎn)化成 Spanned 的格式,然后將其傳遞到 TextView 的 setText方法中,這樣就可以在 TextView 中顯示 HTML 頁面的內(nèi)容了。
需要注意的是,并不是所有的 HTML 標(biāo)簽在 TextView 中都是支持的,且官方文檔并沒有明確的說明支持 HTML 標(biāo)簽列表,通過查看 Android 源代碼,可以得到簡(jiǎn)單的支持列表。
下面的示例來介紹如何在 TextView 中顯示一段 HTML 內(nèi)容,要顯示的這段 HTML 內(nèi)容即包含超鏈接內(nèi)容,也包含有圖片。
2.使用
fromHtml方法
source,就是包含 HTML 內(nèi)容的字符串。Html.ImageGetter 和 Html.TagHandler 是兩個(gè)接口,提供給開發(fā)者繼承使用。
imageGetter, 如果要顯示圖片是需要被繼承的,重寫 getDrawable(String source)方法,用于獲取 HTML 里面的圖片來顯示在 TextView 中。
tagHandler,其作用是把 HTML 帶標(biāo)記的文本內(nèi)容字符串轉(zhuǎn)化成可以顯示效果的的 Spanned 字符串 。由于并非所有的 HTML 標(biāo)簽都可以轉(zhuǎn)化,所以在使用時(shí),用戶需要自己添加一些必要的標(biāo)簽和處理方法時(shí)才會(huì)繼承使用的。
繼承ImageGetter
繼承于 ImageGetter,重寫 getDrawable (String source) 方法。通過異步操作,讀取本地/網(wǎng)絡(luò)資源,獲得drawable對(duì)象。
繼承TagHandler
繼承于 TagHandler,重寫了 handleTag方法。為了支持更多的標(biāo)簽,例如為了支持<ul><ol><dd>和<li>標(biāo)簽,這四個(gè)標(biāo)簽是在 formHtml方法中本身是不支持。
如果開發(fā)者認(rèn)為安卓 TagHandler 提供的默認(rèn)標(biāo)簽解析已經(jīng)夠用,直接在 fromHtml方法中第三個(gè)參數(shù)的地方填寫 既可。
最后,通過 formHtml方法將 HTML 內(nèi)容轉(zhuǎn)化為可供顯示的 SpannableString,將 SpannableString 通過 setText 方法放入 TextView 中,就可以顯示圖文并茂的內(nèi)容了。
用戶交互
formHtml方法已經(jīng)將 HTML 內(nèi)容中的超鏈接和圖片轉(zhuǎn)義成為 UrlSpan 和 ImageSpan,進(jìn)而在 TextView 中完成顯示。但是此時(shí)是沒有任何用戶交互的,用戶只能看到 HTML 的內(nèi)容,下面介紹如何添加用戶交互功能。
要完成用戶交互,這里我們需要在 TextView 中還需要調(diào)用textView.setMovementMethod方法。
Android 提供了 LinkMovementMethod 類以實(shí)現(xiàn)了對(duì)于文本內(nèi)容中超鏈接的遍歷,并且支持對(duì)于超鏈接的點(diǎn)擊事件。
所以只要在添加下面一行代碼,就可以使點(diǎn)擊 UrlSpan 能夠觸發(fā)打開鏈接的功能。
如果想要更多的用戶交互效果,可以自定義LinkMovementMethod 類,重寫onTouchEvent方法來實(shí)現(xiàn)。
3.效果
關(guān)于HTML顯示這部分,沒做具體實(shí)現(xiàn)。具體可以看:靈活高效的在 Android Native App 開發(fā)中顯示 HTML 內(nèi)容(https://www.ibm.com/developerworks/cn/web/1407_zhangqian_androidhtml/index.html),里面有具體源碼可以下載,HTML部分內(nèi)容也是參考該篇文章完成的。
開源庫:html-textview
https://github.com/PrivacyApps/html-textview
6
總結(jié)
以上就是關(guān)于圖文混排的一些解決方案,相信通過這些了解,對(duì)于工作中的實(shí)際場(chǎng)景的使用大家會(huì)有適當(dāng)?shù)慕鉀Q方案。由于實(shí)際應(yīng)用較少,所以認(rèn)識(shí)較為淺顯,可能有些地方描述不當(dāng),后期會(huì)考慮封裝個(gè)解決圖文混排的工具類,加深下理解。
.實(shí)現(xiàn)的效果:
2.實(shí)現(xiàn)代碼my_toast_show_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom|center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="150dp"
android:background="@drawable/shape_toast_rect_corner"
android:gravity="center"
android:minWidth="100dp"
android:orientation="vertical"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:paddingRight="5dp"
android:paddingBottom="5dp">
<TextView
android:id="@+id/textItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:lineSpacingMultiplier="1.2"
android:maxWidth="450dp"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="8dp"
android:textColor="#fff"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
shape_toast_rect_corner.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#9f000000"/>
<corners
android:radius="5dp" />
</shape>
ToastUtil
package com.antbyte.listdemo;
import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class ToastUtil {
//顯示彈出信息
public static void ToastMessage(String message) {
Context context = MyApp.mApp;
try {
View toastRoot = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.my_toast_show_view, null);
Toast toast = new Toast(context);
toast.setView(toastRoot);
TextView tv = (TextView) toastRoot.findViewById(R.id.textItem);
tv.setText(message);
toast.setGravity(Gravity.BOTTOM, 0, 0);
toast.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyApp
package com.antbyte.listdemo;
import android.app.Application;
public class MyApp extends Application {
public static MyApp mApp;
@Override
public void onCreate() {
super.onCreate();
mApp = this;
}
}
3.調(diào)用方法
ToastUtil.ToastMessage("提示信息");
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。