解析在Android中为TextView增加自定义HTML标签的实现方法

 更新时间:2013年05月10日 09:15:53   作者:  
本篇文章是对在Android中为TextView增加自定义HTML标签的方法进行了详细的分析介绍。需要的朋友参考下

Android中的TextView,本身就支持部分的Html格式标签。这其中包括常用的字体大小颜色设置,文本链接等。使用起来也比较方便,只需要使用Html类转换一下即可。比如:

textView.setText(Html.fromHtml(str));


然而,有一种场合,默认支持的标签可能不够用。比如,我们需要在textView中点击某种链接,返回到应用中的某个界面,而不仅仅是网络连接,如何实现?


经过几个小时对android中的Html类源代码的研究,找到了解决办法,并且测试通过。

先看Html类的源代码中有这样一段:

复制代码 代码如下:

/**
    * 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的tag进行解析部分的代码:

复制代码 代码如下:

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 emite the linebreaks when we handle the close tag.  
        } else if (tag.equalsIgnoreCase("p")) { 
            handleP(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("div")) { 
            handleP(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("em")) { 
            start(mSpannableStringBuilder, new Bold()); 
        } else if (tag.equalsIgnoreCase("b")) { 
            start(mSpannableStringBuilder, new Bold()); 
        } else if (tag.equalsIgnoreCase("strong")) { 
            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")) { 
            handleP(mSpannableStringBuilder); 
            start(mSpannableStringBuilder, new Blockquote()); 
        } 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("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') { 
            handleP(mSpannableStringBuilder); 
            start(mSpannableStringBuilder, new Header(tag.charAt(1) - '1')); 
        } else if (tag.equalsIgnoreCase("img")) { 
            startImg(mSpannableStringBuilder, attributes, mImageGetter); 
        } else if (mTagHandler != null) { 
            mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader); 
        } 
    } 

    private void handleEndTag(String tag) { 
        if (tag.equalsIgnoreCase("br")) { 
            handleBr(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("p")) { 
            handleP(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("div")) { 
            handleP(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("em")) { 
            end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); 
        } else if (tag.equalsIgnoreCase("b")) { 
            end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); 
        } else if (tag.equalsIgnoreCase("strong")) { 
            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 
        } else if (tag.equalsIgnoreCase("cite")) { 
            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 
        } else if (tag.equalsIgnoreCase("dfn")) { 
            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 
        } else if (tag.equalsIgnoreCase("i")) { 
            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 
        } else if (tag.equalsIgnoreCase("big")) { 
            end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f)); 
        } else if (tag.equalsIgnoreCase("small")) { 
            end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f)); 
        } else if (tag.equalsIgnoreCase("font")) { 
            endFont(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("blockquote")) { 
            handleP(mSpannableStringBuilder); 
            end(mSpannableStringBuilder, Blockquote.class, new QuoteSpan()); 
        } else if (tag.equalsIgnoreCase("tt")) { 
            end(mSpannableStringBuilder, Monospace.class, 
                    new TypefaceSpan("monospace")); 
        } else if (tag.equalsIgnoreCase("a")) { 
            endA(mSpannableStringBuilder); 
        } else if (tag.equalsIgnoreCase("u")) { 
            end(mSpannableStringBuilder, Underline.class, new UnderlineSpan()); 
        } else if (tag.equalsIgnoreCase("sup")) { 
            end(mSpannableStringBuilder, Super.class, new SuperscriptSpan()); 
        } else if (tag.equalsIgnoreCase("sub")) { 
            end(mSpannableStringBuilder, Sub.class, new SubscriptSpan()); 
        } else if (tag.length() == 2 && 
                Character.toLowerCase(tag.charAt(0)) == 'h' && 
                tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { 
            handleP(mSpannableStringBuilder); 
            endHeader(mSpannableStringBuilder); 
        } else if (mTagHandler != null) { 
            mTagHandler.handleTag(false, tag, mSpannableStringBuilder, mReader); 
        } 
    } 

可以看到,如果不是默认的标签,会调用mTagHandler的handleTag方法。所以,我们可以实现此接口,来解析自己定义的标签类型。

再看一段我实现的对<game>标签进行解析的示例代码:

复制代码 代码如下:

public class GameTagHandler implements TagHandler { 
    private int startIndex = 0; 
    private int stopIndex = 0; 
    @Override 
    public void handleTag(boolean opening, String tag, Editable output, 
            XMLReader xmlReader) { 
        if (tag.toLowerCase().equals("game")) { 
            if (opening) { 
                startGame(tag, output, xmlReader); 
            } else { 
                endGame(tag, output, xmlReader); 
            } 
        }  

    } 
    public void startGame(String tag, Editable output, XMLReader xmlReader) { 
        startIndex = output.length(); 
    } 

    public void endGame(String tag, Editable output, XMLReader xmlReader) { 
        stopIndex = output.length(); 
        output.setSpan(new GameSpan(), startIndex, stopIndex, 
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
    } 

    private class GameSpan extends ClickableSpan implements OnClickListener { 

        @Override 
        public void onClick(View v) { 
            // 跳转某页面  
        } 
    }

上面这段代码,是对<game>…</game>的自定义标签进行解析。


具体调用方法:

       textView.setText(Html.fromHtml(“点击<game>这里</game>跳转到游戏”,

              null, new GameTagHandler()));

       textView.setClickable(true);

       textView.setMovementMethod(LinkMovementMethod.getInstance());


运行后,能够看到文本中的字符串“这里”带了超链接,点击链接后,GameSpan类的onClick()方法被调用。就可以在这个方法中进行跳转了。

相关文章

  • Android SlidingMenu使用和示例详解

    Android SlidingMenu使用和示例详解

    这篇文章主要介绍了Android SlidingMenu使用和示例详解,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-12-12
  • Android自定义VIew实现卫星菜单效果浅析

    Android自定义VIew实现卫星菜单效果浅析

    这篇文章主要介绍了Android自定义VIew实现卫星菜单效果浅析,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • XListView实现多条目网络数据刷新加载 网络加载图片

    XListView实现多条目网络数据刷新加载 网络加载图片

    这篇文章主要为大家详细介绍了XListView实现多条目网络数据刷新加载,网络加载图片,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-11-11
  • Android Socket 线程连接openwrt与arduino单片机串口双向通信的实例解析

    Android Socket 线程连接openwrt与arduino单片机串口双向通信的实例解析

    这篇文章主要介绍了Android Socket 线程连接openwrt与arduino单片机串口双向通信的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • Android应用保活实践详解

    Android应用保活实践详解

    这篇文章主要介绍了Android应用保活实践详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • 基于Android RecyclerView实现宫格拖拽效果

    基于Android RecyclerView实现宫格拖拽效果

    在Android发展的进程中,网格布局一直比较有热度,其中一个原因是对用户来说便捷操作,对app厂商而言也会带来很多的曝光量,本篇我们会使用RecyclerView来实现网格拖拽,本篇将结合图片分片案例,实现拖拽效果,需要的朋友可以参考下
    2024-03-03
  • Android 文件选择的实现代码

    Android 文件选择的实现代码

    这篇文章介绍了Android 文件选择的实现代码,有需要的朋友可以参考一下
    2013-08-08
  • Android实现图片裁剪和上传

    Android实现图片裁剪和上传

    这篇文章主要为大家详细介绍了Android实现图片的裁剪和上传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • android加载系统相册图片并显示详解

    android加载系统相册图片并显示详解

    大家好,本篇文章主要讲的是android加载系统相册图片并显示详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • android MediaRecorder实现录屏时带录音功能

    android MediaRecorder实现录屏时带录音功能

    这篇文章主要介绍了android MediaRecorder录屏时带录音功能实现代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04

最新评论