在前一篇 Android SpannableString浅析中我们采用html实现了文本处理的效果。当时设置部分的代码如下:
private void setText() {
String originText = "#重磅消息#近日谷歌放出Android N的第二个开发者预览版(Developer Preview)";
String effect1 = "<font color='#FF0000'>#重磅消息#</font> <br> 近日谷歌放出Android " +
"N的第二个开发者预览版<a href='http://developer.android.com/index.html'>(Developer Preview)</a>";
String effect2 = "<font color='#303F9F'>#重磅消息#</font> 近日谷歌放出Android " +
"N的第二个开发者预览版<a href='http://developer.android.com/index.html'>(Developer Preview)</a>";
StringBuilder sb = new StringBuilder(originText);
sb.append("<br><br><br><br>");
sb.append(effect1);
sb.append("<br><br><br><br>");
sb.append(effect2);
textView.setText(Html.fromHtml(sb.toString()));
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
这里我们改变了部分文字的显示颜色,同时对另外一部分内容添加了点击事件的处理,并且加入了下划线。今天我就来看看Html代码的解析。
解析过程
在上面的代码中我们设置的时候调用了Html.fromHtml()函数,从这个函数就可以知道这个是将一段html内容解析成TextView可以展示的内容。我们就从这开始逐步看看该类做了哪些事情?能解析的内容是什么样的?
在调用这个函数之前,我们首先看看Html的构造函数private Html() { },可以看到被private修饰,说明在外部不能构造Html实例,我们看到代码中也没有任何返回实例的地方,因此这里的使用方式都是采取静态方法调用。
我们来看看fromHtml()函数:
public static Spanned fromHtml(String source) {
return fromHtml(source, null, null);
}
public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) {
Parser parser = new Parser();
try {
parser.setProperty(Parser.schemaProperty, HtmlParser.schema);
} catch (org.xml.sax.SAXNotRecognizedException e) {
// Should not happen.
throw new RuntimeException(e);
} catch (org.xml.sax.SAXNotSupportedException e) {
// Should not happen.
throw new RuntimeException(e);
}
HtmlToSpannedConverter converter =
new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser);
return converter.convert();
}
Html.fromHtml函数继续调用了三参数的romHtml(String source, ImageGetter imageGetter,TagHandler tagHandler),参数分别为数据源,图片处理,tag处理 。首先构造了一个Parser实例,Parser在org.ccil.cowan.tagsoup包下,我本地的代码是不能导航到的,这里给该代码的一个连接Parser源码。
这里我们额外说一下TagSoup,我们来看看他的官方介绍:
This is the home page of TagSoup, a SAX-compliant parser written in Java that, instead of parsing well-formed or valid XML, parses HTML as it is found in the wild: poor, nasty and brutish, though quite often far from short. TagSoup is designed for people who have to process this stuff using some semblance of a rational application design. By providing a SAX interface, it allows standard XML tools to be applied to even the worst HTML. TagSoup also includes a command-line processor that reads HTML files and can generate either clean HTML or well-formed XML that is a close approximation to XHTML.
对应大致的意思就是:
TagSoup是Java语言编写一个解析Html的工具,他通过SAX引擎解析结构糟糕、令人抓狂的不规范HTML文档。TagSoup可以将一个HTML文档转换为结构良好的XML文档,方便开发人员对获取的HTML文档进行解析等操作。同时TagSoup提供了命令行程序,可以运行TagSoup来对HTML文档进行解析。
构造了Parser后调用了Parser.setProperty函数,传入了schemaProperty,这里是一个字符串,还传入了HTMLSchema对象,HTMLSchema对象罗列了HTML的所有属性节点,HTMLSchema也属于TagSoup,这里不对TagSoup做过多的介绍,知道他是干什么的就好了。
最后构造了一个HtmlToSpannedConverter实例,传入了上面传递进来的参数,数据源,imageGetter, tagHandler, parser,最后调用了HtmlToSpannedConverter的 convert函数。这里我们看这个类名就能大致知道该类做了什么操作,主要是将Html内容转换为Span对象。这里就又回到了前一篇的内容,最终处理的都是Span类型。
这里我们先看看HtmlToSpannedConverter的构造函数都干了什么?
public HtmlToSpannedConverter(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler,
Parser parser) {
mSource = source;
mSpannableStringBuilder = new SpannableStringBuilder();
mImageGetter = imageGetter;
mTagHandler = tagHandler;
mReader = parser;
}
可以看到将传入的参数赋值给对应的变量,同时构造了一个SpannableStringBuilder对象,该对象与StringBuilder类型,StringBuilder主要是连接字符串,减少不必要的空间浪费,SpannableStringBuilder当然就是连接SpannableString,SpannableString的主要内容看前一篇 Android SpannableString浅析,我们继续看看convert函数干了什么?
public Spanned convert() {
mReader.setContentHandler(this);
try {
mReader.parse(new InputSource(new StringReader(mSource)));
} catch (IOException e) {
// We are reading from a string. There should not be IO problems.
throw new RuntimeException(e);
} catch (SAXException e) {
// TagSoup doesn't throw parse exceptions.
throw new RuntimeException(e);
}
// Fix flags and range for paragraph-type markup.
Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class);
for (int i = 0; i < obj.length; i++) {
int start = mSpannableStringBuilder.getSpanStart(obj[i]);
int end = mSpannableStringBuilder.getSpanEnd(obj[i]);
// If the last line of the range is blank, back off by one.
if (end - 2 >= 0) {
if (mSpannableStringBuilder.charAt(end - 1) == '\n' &&
mSpannableStringBuilder.charAt(end - 2) == '\n') {
end--;
}
}
if (end == start) {
mSpannableStringBuilder.removeSpan(obj[i]);
} else {