Android中的TextView,本身就支持部分的Html格式标签。这其中包括常用的字体大小颜色设置,文本链接等。使用起来也比较方便,只需要使用Html类转换一下即可。比如:
textView.setText(Html.fromHtml(str));
然而,有一种场合,默认支持的标签可能不够用。比如,我们需要在textView中点击某种链接,返回到应用中的某个界面,而不仅仅是网络连接,如何实现?
经过几个小时对android中的Html类源代码的研究,找到了解决办法,并且测试通过。
先看Html类的源代码中有这样一段:
1/** 2 * Is notified when HTML tags are encountered that the parser does 3 * not know how to interpret. 4 */ 5 public static interface TagHandler { 6 /** 7 * This method will be called whenn the HTML parser encounters 8 * a tag that it does not know how to interpret. 9 */ 10 public void handleTag(boolean opening, String tag, 11 Editable output, XMLReader xmlReader); 12}
这里定义了一个接口,接口用于什么呢?
再继续看代码,看到对Html的tag进行解析部分的代码:
1private void handleStartTag(String tag, Attributes attributes) { 2 if (tag.equalsIgnoreCase("br")) { 3 // We don't need to handle this. TagSoup will ensure that there's a </br> for each <br> 4 // so we can safely emite the linebreaks when we handle the close tag. 5 } else if (tag.equalsIgnoreCase("p")) { 6 handleP(mSpannableStringBuilder); 7 } else if (tag.equalsIgnoreCase("div")) { 8 handleP(mSpannableStringBuilder); 9 } else if (tag.equalsIgnoreCase("em")) { 10 start(mSpannableStringBuilder, new Bold()); 11 } else if (tag.equalsIgnoreCase("b")) { 12 start(mSpannableStringBuilder, new Bold()); 13 } else if (tag.equalsIgnoreCase("strong")) { 14 start(mSpannableStringBuilder, new Italic()); 15 } else if (tag.equalsIgnoreCase("cite")) { 16 start(mSpannableStringBuilder, new Italic()); 17 } else if (tag.equalsIgnoreCase("dfn")) { 18 start(mSpannableStringBuilder, new Italic()); 19 } else if (tag.equalsIgnoreCase("i")) { 20 start(mSpannableStringBuilder, new Italic()); 21 } else if (tag.equalsIgnoreCase("big")) { 22 start(mSpannableStringBuilder, new Big()); 23 } else if (tag.equalsIgnoreCase("small")) { 24 start(mSpannableStringBuilder, new Small()); 25 } else if (tag.equalsIgnoreCase("font")) { 26 startFont(mSpannableStringBuilder, attributes); 27 } else if (tag.equalsIgnoreCase("blockquote")) { 28 handleP(mSpannableStringBuilder); 29 start(mSpannableStringBuilder, new Blockquote()); 30 } else if (tag.equalsIgnoreCase("tt")) { 31 start(mSpannableStringBuilder, new Monospace()); 32 } else if (tag.equalsIgnoreCase("a")) { 33 startA(mSpannableStringBuilder, attributes); 34 } else if (tag.equalsIgnoreCase("u")) { 35 start(mSpannableStringBuilder, new Underline()); 36 } else if (tag.equalsIgnoreCase("sup")) { 37 start(mSpannableStringBuilder, new Super()); 38 } else if (tag.equalsIgnoreCase("sub")) { 39 start(mSpannableStringBuilder, new Sub()); 40 } else if (tag.length() == 2 && 41 Character.toLowerCase(tag.charAt(0)) == 'h' && 42 tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { 43 handleP(mSpannableStringBuilder); 44 start(mSpannableStringBuilder, new Header(tag.charAt(1) - '1')); 45 } else if (tag.equalsIgnoreCase("img")) { 46 startImg(mSpannableStringBuilder, attributes, mImageGetter); 47 } else if (mTagHandler != null) { 48 mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader); 49 } 50 } 51 52 private void handleEndTag(String tag) { 53 if (tag.equalsIgnoreCase("br")) { 54 handleBr(mSpannableStringBuilder); 55 } else if (tag.equalsIgnoreCase("p")) { 56 handleP(mSpannableStringBuilder); 57 } else if (tag.equalsIgnoreCase("div")) { 58 handleP(mSpannableStringBuilder); 59 } else if (tag.equalsIgnoreCase("em")) { 60 end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); 61 } else if (tag.equalsIgnoreCase("b")) { 62 end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); 63 } else if (tag.equalsIgnoreCase("strong")) { 64 end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 65 } else if (tag.equalsIgnoreCase("cite")) { 66 end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 67 } else if (tag.equalsIgnoreCase("dfn")) { 68 end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 69 } else if (tag.equalsIgnoreCase("i")) { 70 end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 71 } else if (tag.equalsIgnoreCase("big")) { 72 end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f)); 73 } else if (tag.equalsIgnoreCase("small")) { 74 end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f)); 75 } else if (tag.equalsIgnoreCase("font")) { 76 endFont(mSpannableStringBuilder); 77 } else if (tag.equalsIgnoreCase("blockquote")) { 78 handleP(mSpannableStringBuilder); 79 end(mSpannableStringBuilder, Blockquote.class, new QuoteSpan()); 80 } else if (tag.equalsIgnoreCase("tt")) { 81 end(mSpannableStringBuilder, Monospace.class, 82 new TypefaceSpan("monospace")); 83 } else if (tag.equalsIgnoreCase("a")) { 84 endA(mSpannableStringBuilder); 85 } else if (tag.equalsIgnoreCase("u")) { 86 end(mSpannableStringBuilder, Underline.class, new UnderlineSpan()); 87 } else if (tag.equalsIgnoreCase("sup")) { 88 end(mSpannableStringBuilder, Super.class, new SuperscriptSpan()); 89 } else if (tag.equalsIgnoreCase("sub")) { 90 end(mSpannableStringBuilder, Sub.class, new SubscriptSpan()); 91 } else if (tag.length() == 2 && 92 Character.toLowerCase(tag.charAt(0)) == 'h' && 93 tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { 94 handleP(mSpannableStringBuilder); 95 endHeader(mSpannableStringBuilder); 96 } else if (mTagHandler != null) { 97 mTagHandler.handleTag(false, tag, mSpannableStringBuilder, mReader); 98 } 99 }
可以看到,如果不是默认的标签,会调用mTagHandler的handleTag方法。所以,我们可以实现此接口,来解析自己定义的标签类型。
再看一段我实现的对<game>标签进行解析的示例代码:
1public class GameTagHandler implements TagHandler { 2 3 private int startIndex = 0; 4 5 private int stopIndex = 0; 6 7 @Override 8 public void handleTag(boolean opening, String tag, Editable output, 9 XMLReader xmlReader) { 10 if (tag.toLowerCase().equals("game")) { 11 if (opening) { 12 startGame(tag, output, xmlReader); 13 } else { 14 endGame(tag, output, xmlReader); 15 } 16 } 17 18 } 19 20 public void startGame(String tag, Editable output, XMLReader xmlReader) { 21 startIndex = output.length(); 22 } 23 24 public void endGame(String tag, Editable output, XMLReader xmlReader) { 25 stopIndex = output.length(); 26 output.setSpan(new GameSpan(), startIndex, stopIndex, 27 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 28 } 29 30 31 private class GameSpan extends ClickableSpan implements OnClickListener { 32 33 @Override 34 public void onClick(View v) { 35 // 跳转某页面 36 } 37 }
上面这段代码,是对<game>…</game>的自定义标签进行解析。
具体调用方法:
textView.setText(Html.fromHtml(“点击<game>这里</game>跳转到游戏”,
null, new GameTagHandler()));
textView.setClickable(true);
textView.setMovementMethod(LinkMovementMethod.getInstance());
运行后,能够看到文本中的字符串“这里”带了超链接,点击链接后,GameSpan类的onClick()方法被调用。就可以在这个方法中进行跳转了。
$(document).ready(function(){dp.SyntaxHighlighter.HighlightAll('code');});
原文链接: http://blog.csdn.net/arui319/article/details/6709424