富文本處理TextView顯示富文本的三種方案

TextView顯示富文本的三種方案

背景

​ 在做Android開發的時候,大家可能會經常遇到這種需求:一個textview控件上想展示一段聲明,聲明的大部分內容是正常的很色字體,書名號引用的各種文件條款,使用藍色字體顯示,類似如下效果圖。

在這裏插入圖片描述

​ 這類需求一般有三種實現方式

方式一:使用多個TextView來顯示

​ 這是最爲簡單無腦的方法,但是很試用場景比較有限,當涉及到文字換行時,這種實現方式不方便處理了,這裏就不給大家詳細演示了。

方式二:SpannableString

​ 該方法的使用詳情可以參考這篇文檔 https://www.jianshu.com/p/472fd3e32324 這裏也就不給大家詳細介紹了,從這篇博文中也能看出SpannableString的侷限了性。沒錯,SpannableString的優點在於控制得精細,缺點也是在這。我們使用SpannableString的時候必須指定樣式使用的字符下標,那如果我們的字符串不是固定長度的呢?方式二就不太適用了。

方式三:使用Html.fromHtml

​ 方式三是今天主要講解的一個方案,將要展示的文案寫成html的格式,通過內置的Html類,使用fromHtml方法將html文本轉換爲可顯示在textview的帶有各種標記的文字。

原理:html是xml語言,fromHtml方法中,使用sax方式對html的xml進行解析,在解析到能識別的標籤時,根據不同的Spanned策略來標記文案

​ 簡單的實現方式如下:

String content = "一段html風格的字符串";
textView.setText(Html.fromHtml(content, null, null));
監聽自定義標籤

** 原理**:當html.fromHtml方法在處理html文件時,如果開發者傳遞了自定義的TagHandler,在解析的過程中,會返回不能識別的標籤到TagHandler的handleTag方法中,開發者在這個回調中,可以自定義處理,該接口的原生定義如下。

public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) 
/**
 * 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);
}
監聽已有標籤的自定義屬性

原理:在監聽自定義標籤的基礎上,在解析html文件時,會先返回不能解析的html標籤,這樣,開發者在這個回調中,可以在很早的時機拿到XmlReader,XmlReader是負責解析整個html文件的,其內部有一個監聽器ContentHandler,用於監聽整個解析過程,我們可以將xmlReader中的原始監聽器進行保存,在需要特殊處理的時候做攔截,這樣,變達到了監聽已有標籤的自定義屬性的能力

​ 具體代碼示例如下:

public class HtmlTagHandler implements Html.TagHandler {

    private static final String TAG = "HtmlTagHandler";
    private volatile ContentHandler mOriginalContentHandler; //原始的監聽器


    //監聽html解析信息,接手後續處理
    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
        VLog.d(TAG, "opening: " + opening + " tag: " + tag);
        //獲取對xmlReader解析的控制權
        if (mOriginalContentHandler == null) {
            mOriginalContentHandler = xmlReader.getContentHandler();
            xmlReader.setContentHandler(new HtmlContentHandlerWrapper(mOriginalContentHandler));//獲取控制權,讓HtmlContentHandlerWrapper監聽解析流程
        }
    }

}
public class HtmlContentHandlerWrapper implements ContentHandler {

    private static final String TAG = "HtmlContentHandlerWrapper";
    private volatile ContentHandler mOriginalContentHandler; //原始的監聽器
    private static final String ORGINAL_URI = "自定義的屬性"; //標籤內部原始字段
    private static final String A_TAG = "a"; //需要特殊處理的標籤

    public HtmlContentHandlerWrapper(ContentHandler handler) {
        mOriginalContentHandler = handler;
    }

    /**
     * 舉例:攔截a標籤,識別自定義字段
     * @param uri
     * @param localName
     * @param qName
     * @param atts
     * @throws SAXException
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        if (canHandleTag(localName)) {  //攔截,判斷是否可以解析該標籤
            VLog.d(TAG, "startElement custom");
            AttributesImpl a = (AttributesImpl) atts;
            String orignValue = a.getValue("", ORGINAL_URI);
            if (!TextUtils.isEmpty(orignValue)) { //存在自定義的屬性時做該操作
              //自定義的處理
                }
            }

        }
        //原流程處理
        this.mOriginalContentHandler.startElement(uri, localName, qName, atts);
    }


    private boolean canHandleTag(String tagName) {
        return A_TAG.equalsIgnoreCase(tagName);
    }



    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        this.mOriginalContentHandler.endElement(uri, localName, qName);
    }

    @Override
    public void setDocumentLocator(Locator locator) {
        mOriginalContentHandler.setDocumentLocator(locator);
    }

    @Override
    public void startDocument() throws SAXException {
        mOriginalContentHandler.startDocument();
    }

    @Override
    public void endDocument() throws SAXException {
        mOriginalContentHandler.endDocument();
    }

    @Override
    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        mOriginalContentHandler.startPrefixMapping(prefix, uri);
    }

    @Override
    public void endPrefixMapping(String prefix) throws SAXException {
        mOriginalContentHandler.endPrefixMapping(prefix);
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        mOriginalContentHandler.characters(ch, start, length);
    }

    @Override
    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
        mOriginalContentHandler.ignorableWhitespace(ch, start, length);
    }

    @Override
    public void processingInstruction(String target, String data) throws SAXException {
        mOriginalContentHandler.processingInstruction(target, data);
    }

    @Override
    public void skippedEntity(String name) throws SAXException {
        mOriginalContentHandler.skippedEntity(name);

    }


}
相關知識xml文檔解析的三種方案

SAX**:(Simple API for XML****)**這種解析方式基於事件的模型。通俗的講就是XML文件在加載的過程中,加載到不同節點會相應觸發不同方法來處理。它屬於一次加載。它可以處理任意大小的XML文件,它對內存的要求非常低,因爲SAX採用的是讀取文件的方式,也就是當它是文本文件在讀,讀完就完了,什麼信息都沒有保存。當然它也有其缺點,解析過程中無法中斷,只能讀取XML文件而不能修改,編碼上也相對複雜與難於理解。

DOM**:(Document Object Model****)**文檔對象模型,它是基於對象的,又或者基於樹的。它屬於兩次加載,首先把文檔載入內存,第二次把文檔解析形成一棵樹。如果文檔過大對內存佔用是很大的。但它也有其優點,它可以解析的過程中修改文件樹,可以隨便存儲文件樹的任意部分,相對容易理解。

**Pull****解析:**android中內置了pull解析包。這也是android程序中所推薦的xml解析方式。從它的字面上就可以看出來,其優點,pull,拉的意思。我要什麼資源我就拿什麼資源。我只需要xml文件中一部分,我就拉一部分。從而節省資源,提高效率。當然在J2EE中也可以使用Pull解析。Pull解析也非常易於理解。

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