Android自定义解析Html的TextView-HtmlView

在项目中有时需要显示一些Html文本,但是时webView的性能和效率都不太好,所以就需要使用Textview来显示Html文本,好在Andriod本身提供了一个简单的Html解析的方法:
Html.fromHtml(text, ImageGetter, TagHandler);
下面是通过重写TextView的方式实现自动解析Html文本的自定义组件HtmlView,该组件主要实现的功能是解析<img/>标签显示图片并添加图片的点击事件,另外在代码中也示范了一种解析自定义标签的方法(此方法参考stackoverflow上某个问题解答);例子程序在最后,下面是核心代码:
Android自定义解析Html的TextView-HtmlView_第1张图片Android自定义解析Html的TextView-HtmlView_第2张图片
 /**
     * Sets the text that this TextView is to display (see
     * {@link #setText(CharSequence)}) and also sets whether it is stored
     * in a styleable/spannable buffer and whether it is editable.
     *
     * @param text
     * @param type
     * @attr ref android.R.styleable#TextView_text
     * @attr ref android.R.styleable#TextView_bufferType
     */
    @Override
    public void setText(CharSequence text, BufferType type) {
        if(null == text){
            this.mText = "";
        }else{
            this.mText = text;
        }
        this.mText = text;
        Spanned spanned = Html.fromHtml(this.mText.toString(), new MyImageGetter(), new MyTagHandler());
        super.setText(spanned, type);
    }

    @Override
    public boolean isClickable() {
        return true;
    }

    @Override
    protected MovementMethod getDefaultMovementMethod() {
        return LinkMovementMethod.getInstance();
    }

    class MyImageGetter implements Html.ImageGetter {

        /**
         * This method is called when the HTML parser encounters an
         * <img> 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.
         *
         * @param source
         */
        @Override
        public Drawable getDrawable(String source) {
            /*在这里根据source来加载图片,并返回*/
            /*简单测试,返回程序ic_lanucher*/
            /*网络图片需要异步加载,在此处发起异步加载线程,图片加载完成后再设置一次setText,
                当再次执行到此处时,将加载好的图片(应存放在缓存中)返回就行了*/
            Drawable drawable = null;
            if(Build.VERSION.SDK_INT < 21){
                drawable = getResources().getDrawable(R.mipmap.ic_launcher);
                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight());
            }else{
                drawable = getResources().getDrawable(R.mipmap.ic_launcher, null);
                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight());
            }

            return drawable;
        }
    }

    /**
     * 用来通知当解析器遇到无法识别的标签时该作出何种处理
     */
    class MyTagHandler implements Html.TagHandler{

        /**
         * 参数:
         * opening:为true时表示某个标签开始解析,为false时表示该标签解析完
         * tag:当前解析的标签
         * output:文本中的内容
         * xmlReader:xml解析器
         */
        @Override
        public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
            Log.e("TAG-->", tag);
            Log.e("output-->", output.toString());
            if (tag.toLowerCase().equals("img")) {//解析<img/>标签(注意标签格式不是<img></img>)
                Log.e("opening-->", opening + "");
                int len = output.length();

                ImageSpan[] images = output.getSpans(len-1, len, ImageSpan.class);
                Log.e("images-->", images.length + "");
                String imgURL = images[0].getSource();
                Log.e("imgURL-->", imgURL + "");
                //添加点击事件
                output.setSpan(new ImageClickSpan(mContext, imgURL), len-1, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            } else if(tag.equalsIgnoreCase("strike")) {//自定义解析<strike></strike>标签
                int len = output.length();
                Log.e("opening-->", opening + "");
                if(opening) {//开始解析该标签,打一个标记
                    output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK);
                } else {//解析结束,读出所有标记,取最后一个标记为当前解析的标签的标记(因为解析方式是便读便解析)
                    StrikethroughSpan[] spans = output.getSpans(0, len, StrikethroughSpan.class);
                    if (spans.length > 0) {
                        for(int i = spans.length - 1; i >= 0; i--){
                            if(output.getSpanFlags(spans[i]) == Spannable.SPAN_MARK_MARK) {
                                int start = output.getSpanStart(spans[i]);
                                output.removeSpan(spans[i]);
                                if (start != len) {
                                    output.setSpan(new StrikethroughSpan(), start, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                                }
                                break;
                            }
                        }
                    }
                }
            }else{//其他标签不再处理
                Log.e("TAG-->", tag + "--不做处理");
            }
        }
    }

    class ImageClickSpan extends ClickableSpan{

        private Context context;
        private String url;


        public ImageClickSpan(Context context, String url){
            this.context = context;
            this.url = url;

        }

        @Override
        public void onClick(View widget) {
            Log.e("TAG-->", "ImageClickSpan");
            showPicDialog(url);

        }


    }


demo源码下载:
Github地址
CSDN下载地址

你可能感兴趣的:(html,android,textview,spannable)