日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Andoid TextView显示富文本html内容及问题处理

發布時間:2025/3/12 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Andoid TextView显示富文本html内容及问题处理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 富文本內容與效果
  • TextView + Html
  • ImageGetter 處理圖片(表情)
  • TagHandler 處理html內容的節點
  • Html的轉換過程
    • HtmlToSpannedConverter
      • handleStartTag
      • startCssStyle(mSpannableStringBuilder, attributes)字體無效果實現
      • getForegroundColorPattern顏色不顯示的坑
  • 處理辦法
    • 顏色修改
    • 粗體支持
    • 斜體支持

來了來了,
html頁面內容不是用用webview的嗎,TextView顯示html什么鬼?
老鐵莫急,沒錯,html頁面內容多數情況下都是用webview來顯示的,尤其是app里面常見的"關于"、“隱私政策”等,這都是單一的顯示,或者整個頁面就顯示這么一個page頁面,自然也就選擇webview。

當遇到富文本這樣的html內容片段,而且是以列表方式顯示多段內容不一樣的內容時候,怎么辦呢。基于源生的習慣,自然就是TextView + Html了。
有坑,但問題不大。

富文本內容與效果

如下一段html,粗體、斜體、橘色和帶了一個表情:

<SPAN style="FONT-SIZE: 10pt; FONT-WEIGHT: bold; COLOR: #ff8000; FONT-STYLE: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy </SPAN>

效果:

TextView + Html

TextView 就不介紹了,主要介紹Html這個類。
Html.java在android.text這個package下,包名也就看出是和文字相關的類。其核心本質是解析html內容,根據html的style給構造出Spanned,Spanned又是什么東西?Spanned是CharSequence的孩子。
平常給TextView設置文字 setText(CharSequence text)這個函數的參數就是CharSequence ,只不過實際傳遞的是CharSequence 的另外一個孩子String。

Html.java 提供html內容和Spanned的相互轉換:
html->Spanned:

public static Spanned fromHtml(String source, int flags)
public static Spanned fromHtml(String source, int flags, ImageGetter imageGetter,TagHandler tagHandler)

Spanned->html:

public static String toHtml(Spanned text, int option)

但實際上上述內容出表情外,其他的效果完全沒有,包括顏色。具體請往后看

ImageGetter 處理圖片(表情)

當遇到<img>標簽的時候就會回調, 對應的返回一個Drawable對象。source 參數就是<img>的src屬性的內容,也就是圖片路徑。根據上文給出的內容,這里source是“emotion\emotion.rb.gif”。同時注意返回的Drawable對象一定要給定邊界,也就是drawable.setBounds(),不然不會顯示圖片。如果這里返回一個null,一般出現一個小矩形。

/*** Retrieves images for HTML &lt;img&gt; tags.*/public static interface ImageGetter {/*** This method is called when the HTML parser encounters an* &lt;img&gt; tag. The <code>source</code> argument is the* string from the "src" attribute; the return value should be* a Drawable representation of the image or <code>null</code>* for a generic replacement image. Make sure you call* setBounds() on your Drawable if it doesn't already have* its bounds set.*/public Drawable getDrawable(String source);}

TagHandler 處理html內容的節點

這個標簽捕獲是有條件的,如果html內容的標簽沒有在Html中定義捕才回調出來,在顯示效果上是沒效的,需要自行處理這個標簽,對應的編輯output。

/*** Is notified when HTML tags are encountered that the parser does* not know how to interpret.*/public static interface TagHandler {/*** This method will be called whenn the HTML parser encounters* a tag that it does not know how to interpret.*/public void handleTag(boolean opening, String tag,Editable output, XMLReader xmlReader);}

注釋講的清楚,parser 識別不了的就會回調通知。

Html的轉換過程

從fromHtml入口可以看出,是HtmlToSpannedConverter 在工作,執行convert

public static Spanned fromHtml(String source, int flags, ImageGetter imageGetter,TagHandler tagHandler) {Parser parser = new Parser();try {parser.setProperty(Parser.schemaProperty, HtmlParser.schema);} catch (org.xml.sax.SAXNotRecognizedException e) {// Should not happen.throw new RuntimeException(e);} catch (org.xml.sax.SAXNotSupportedException e) {// Should not happen.throw new RuntimeException(e);}HtmlToSpannedConverter converter =new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser, flags);return converter.convert();}

HtmlToSpannedConverter

從代碼上看HtmlToSpannedConverter 還不是內部類,是和Html平行定義的。且實現了ContentHandler,ContentHandler就是xml解析回調接口,而該類主要處理了3個回調,其余空實現:

public void startElement(String uri, String localName, String qName, Attributes attributes)throws SAXException {handleStartTag(localName, attributes);}public void endElement(String uri, String localName, String qName) throws SAXException {handleEndTag(localName);}public void characters(char ch[], int start, int length) throws SAXException {StringBuilder sb = new StringBuilder();/** Ignore whitespace that immediately follows other whitespace;* newlines count as spaces.*/for (int i = 0; i < length; i++) {char c = ch[i + start];if (c == ' ' || c == '\n') {char pred;int len = sb.length();if (len == 0) {len = mSpannableStringBuilder.length();if (len == 0) {pred = '\n';} else {pred = mSpannableStringBuilder.charAt(len - 1);}} else {pred = sb.charAt(len - 1);}if (pred != ' ' && pred != '\n') {sb.append(' ');}} else {sb.append(c);}}mSpannableStringBuilder.append(sb);}

當開始一個標簽時調用 handleStartTag
結束一個標簽時調用handleEndTag
解析到文本的時候就追加到mSpannableStringBuilder中

handleStartTag

這里能看出Html類處理了多少標簽,同時也應證沒有定義的標簽都拋給外部處理
注意這里標簽的匹配不區分大小寫 (equalsIgnoreCase)

private void handleStartTag(String tag, Attributes attributes) {if (tag.equalsIgnoreCase("br")) {// We don't need to handle this. TagSoup will ensure that there's a </br> for each <br>// so we can safely emit the linebreaks when we handle the close tag.} else if (tag.equalsIgnoreCase("p")) {startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph());startCssStyle(mSpannableStringBuilder, attributes);} else if (tag.equalsIgnoreCase("ul")) {startBlockElement(mSpannableStringBuilder, attributes, getMarginList());} else if (tag.equalsIgnoreCase("li")) {startLi(mSpannableStringBuilder, attributes);} else if (tag.equalsIgnoreCase("div")) {startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv());} else if (tag.equalsIgnoreCase("span")) {startCssStyle(mSpannableStringBuilder, attributes);} else if (tag.equalsIgnoreCase("strong")) {start(mSpannableStringBuilder, new Bold());} else if (tag.equalsIgnoreCase("b")) {start(mSpannableStringBuilder, new Bold());} else if (tag.equalsIgnoreCase("em")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("cite")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("dfn")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("i")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("big")) {start(mSpannableStringBuilder, new Big());} else if (tag.equalsIgnoreCase("small")) {start(mSpannableStringBuilder, new Small());} else if (tag.equalsIgnoreCase("font")) {startFont(mSpannableStringBuilder, attributes);} else if (tag.equalsIgnoreCase("blockquote")) {startBlockquote(mSpannableStringBuilder, attributes);} else if (tag.equalsIgnoreCase("tt")) {start(mSpannableStringBuilder, new Monospace());} else if (tag.equalsIgnoreCase("a")) {startA(mSpannableStringBuilder, attributes);} else if (tag.equalsIgnoreCase("u")) {start(mSpannableStringBuilder, new Underline());} else if (tag.equalsIgnoreCase("del")) {start(mSpannableStringBuilder, new Strikethrough());} else if (tag.equalsIgnoreCase("s")) {start(mSpannableStringBuilder, new Strikethrough());} else if (tag.equalsIgnoreCase("strike")) {start(mSpannableStringBuilder, new Strikethrough());} else if (tag.equalsIgnoreCase("sup")) {start(mSpannableStringBuilder, new Super());} else if (tag.equalsIgnoreCase("sub")) {start(mSpannableStringBuilder, new Sub());} else if (tag.length() == 2 &&Character.toLowerCase(tag.charAt(0)) == 'h' &&tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {startHeading(mSpannableStringBuilder, attributes, tag.charAt(1) - '1');} else if (tag.equalsIgnoreCase("img")) {startImg(mSpannableStringBuilder, attributes, mImageGetter);} else if (mTagHandler != null) {//除以上標簽以外,都回調給外部處理mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader);}}

明明<SPAN> 是被解析的(tag.equalsIgnoreCase(“span”),就是沒有顏色和粗體、斜體呢?再看startCssStyle(mSpannableStringBuilder, attributes)

startCssStyle(mSpannableStringBuilder, attributes)字體無效果實現

private void startCssStyle(Editable text, Attributes attributes) {String style = attributes.getValue("", "style");if (style != null) {Matcher m = getForegroundColorPattern().matcher(style);if (m.find()) {int c = getHtmlColor(m.group(1));if (c != -1) {start(text, new Foreground(c | 0xFF000000));}}m = getBackgroundColorPattern().matcher(style);if (m.find()) {int c = getHtmlColor(m.group(1));if (c != -1) {start(text, new Background(c | 0xFF000000));}}m = getTextDecorationPattern().matcher(style);if (m.find()) {String textDecoration = m.group(1);if (textDecoration.equalsIgnoreCase("line-through")) {start(text, new Strikethrough());}}}}

取出style 屬性,且此處指處理顏色,并沒有處理字體,字體肯定是不會有效果的了。接著看getForegroundColorPattern。

getForegroundColorPattern顏色不顯示的坑

再看取屬性里面的顏色是通過正則表達式來取的,前景色正則表達式: “(?:\s+|\A)color\s*:\s*(\S*)\b”)

private static Pattern getForegroundColorPattern() {if (sForegroundColorPattern == null) {sForegroundColorPattern = Pattern.compile("(?:\\s+|\\A)color\\s*:\\s*(\\S*)\\b");}return sForegroundColorPattern;}

這里是個坑,color是小寫,內容中的是COLOR,沒有匹配到顏色。同時背景色也是小寫的color。

處理辦法

調用還是不變,但要對原始內容進行修改

顏色修改

直接將style 屬性的值修改為小寫

<!--這樣就可以顯示顏色了--> <SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN>

粗體支持

從代碼上看是明確不處理style 屬性中的粗體,但從handleStartTag解析中有如下片段

else if (tag.equalsIgnoreCase("strong")) {start(mSpannableStringBuilder, new Bold());} else if (tag.equalsIgnoreCase("b")) {start(mSpannableStringBuilder, new Bold());}

基于這個片段,判斷style中屬性中的font-weight如果是bold值,那么直接在原內容基礎上包裹標簽<strong>或<b>。
具體如下:

<b><SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN> </b>//或 <strong><SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN> </strong>

斜體支持

思路和粗體一樣,選擇多一點
代碼片段:

else if (tag.equalsIgnoreCase("em")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("cite")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("dfn")) {start(mSpannableStringBuilder, new Italic());} else if (tag.equalsIgnoreCase("i")) {start(mSpannableStringBuilder, new Italic());}

基于這個片段,判斷style中屬性中的font-style如果是italic值,那么直接在原內容基礎上包裹標簽<em>,<cite>,<dfn>,<i>之一
具體如下:

<em><b><SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN></b> </em> //或 <cite><b><SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN></b> </cite>//或 <dfn><b><SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN></b> </dfn> //或 <i><b><SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotion\emotion.rb.gif" thePath custom="false">boy</SPAN></b> </i>

至此,這段html的顏色、粗體、斜體都能顯示了。

總結

以上是生活随笔為你收集整理的Andoid TextView显示富文本html内容及问题处理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。