富文本处理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解析也非常易于理解。

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