Android中为TextView增加自定义的HTML标签

本文详细介绍了如何在Android中通过自定义HTML标签解析接口,实现TextView中特定标签触发自定义链接跳转功能,包括实现示例代码、调用方法及效果展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()方法被调用。就可以在这个方法中进行跳转了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值