解析在Android中为TextView增加自定义HTML标签的实现方法
程序员文章站
2023-12-10 21:55:22
android中的textview,本身就支持部分的html格式标签。这其中包括常用的字体大小颜色设置,文本链接等。使用起来也比较方便,只需要使用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()方法被调用。就可以在这个方法中进行跳转了。