概述
目录
- 富文本内容与效果
- TextView + Html
- ImageGetter 处理图片(表情)
- TagHandler 处理html内容的节点
- Html的转换过程
- HtmlToSpannedConverter
- handleStartTag
- startCssStyle(mSpannableStringBuilder, attributes)字体无效果实现
- getForegroundColorPattern颜色不显示的坑
- 处理办法
- 颜色修改
- 粗体支持
- 斜体支持
来了来了,
html页面内容不是用用webview的吗,TextView显示html什么鬼?
老铁莫急,没错,html页面内容多数情况下都是用webview来显示的,尤其是app里面常见的"关于"、“隐私政策”等,这都是单一的显示,或者整个页面就显示这么一个page页面,自然也就选择webview。
当遇到富文本
这样的html内容片段,而且是以列表方式显示多段内容不一样的内容时候,怎么办呢。基于源生的习惯,自然就是TextView + Html了。
有坑,但问题不大。
富文本内容与效果
如下一段html,粗体、斜体、橘色和带了一个表情:
<SPAN style="FONT-SIZE: 10pt; FONT-WEIGHT: bold; COLOR: #ff8000; FONT-STYLE: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
效果:
TextView + Html
TextView 就不介绍了,主要介绍Html这个类。
Html.java在android.text这个package下,包名也就看出是和文字相关的类。其核心本质是解析html内容,根据html的style给构造出Spanned,Spanned又是什么东西?Spanned是CharSequence的孩子。
平常给TextView设置文字 setText(CharSequence text)
这个函数的参数就是CharSequence ,只不过实际传递的是CharSequence 的另外一个孩子String。
Html.java 提供html内容和Spanned的相互转换:
html->Spanned:
public static Spanned fromHtml(String source, int flags)
public static Spanned fromHtml(String source, int flags, ImageGetter imageGetter,TagHandler tagHandler)
Spanned->html:
public static String toHtml(Spanned text, int option)
但实际上上述内容出表情外,其他的效果完全没有,包括颜色。具体请往后看
ImageGetter 处理图片(表情)
当遇到<img>标签的时候就会回调, 对应的返回一个Drawable对象。source 参数就是<img>的src属性的内容,也就是图片路径。根据上文给出的内容,这里source是“emotionemotion.rb.gif”。同时注意返回的Drawable对象一定要给定边界,也就是drawable.setBounds(),不然不会显示图片。如果这里返回一个null,一般出现一个小矩形。
/**
* Retrieves images for HTML <img> tags.
*/
public static interface ImageGetter {
/**
* This method is called when the HTML parser encounters an
* <img> tag. The <code>source</code> argument is the
* string from the "src" attribute; the return value should be
* a Drawable representation of the image or <code>null</code>
* for a generic replacement image. Make sure you call
* setBounds() on your Drawable if it doesn't already have
* its bounds set.
*/
public Drawable getDrawable(String source);
}
TagHandler 处理html内容的节点
这个标签捕获是有条件的,如果html内容的标签没有在Html中定义捕才回调出来,在显示效果上是没效的,需要自行处理这个标签,对应的编辑output。
/**
* 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);
}
注释讲的清楚,parser 识别不了的就会回调通知。
Html的转换过程
从fromHtml入口可以看出,是HtmlToSpannedConverter 在工作,执行convert
public static Spanned fromHtml(String source, int flags, 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, flags);
return converter.convert();
}
HtmlToSpannedConverter
从代码上看HtmlToSpannedConverter 还不是内部类,是和Html平行定义的。且实现了ContentHandler,ContentHandler就是xml解析回调接口,而该类主要处理了3个回调,其余空实现:
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
handleStartTag(localName, attributes);
}
public void endElement(String uri, String localName, String qName) throws SAXException {
handleEndTag(localName);
}
public void characters(char ch[], int start, int length) throws SAXException {
StringBuilder sb = new StringBuilder();
/*
* Ignore whitespace that immediately follows other whitespace;
* newlines count as spaces.
*/
for (int i = 0; i < length; i++) {
char c = ch[i + start];
if (c == ' ' || c == 'n') {
char pred;
int len = sb.length();
if (len == 0) {
len = mSpannableStringBuilder.length();
if (len == 0) {
pred = 'n';
} else {
pred = mSpannableStringBuilder.charAt(len - 1);
}
} else {
pred = sb.charAt(len - 1);
}
if (pred != ' ' && pred != 'n') {
sb.append(' ');
}
} else {
sb.append(c);
}
}
mSpannableStringBuilder.append(sb);
}
当开始一个标签时调用 handleStartTag
结束一个标签时调用handleEndTag
解析到文本的时候就追加到mSpannableStringBuilder
中
handleStartTag
这里能看出Html类处理了多少标签,同时也应证没有定义的标签都抛给外部处理
注意这里标签的匹配不区分大小写 (equalsIgnoreCase)
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 emit the linebreaks when we handle the close tag.
} else if (tag.equalsIgnoreCase("p")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph());
startCssStyle(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("ul")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginList());
} else if (tag.equalsIgnoreCase("li")) {
startLi(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("div")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv());
} else if (tag.equalsIgnoreCase("span")) {
startCssStyle(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("strong")) {
start(mSpannableStringBuilder, new Bold());
} else if (tag.equalsIgnoreCase("b")) {
start(mSpannableStringBuilder, new Bold());
} else if (tag.equalsIgnoreCase("em")) {
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")) {
startBlockquote(mSpannableStringBuilder, attributes);
} 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("del")) {
start(mSpannableStringBuilder, new Strikethrough());
} else if (tag.equalsIgnoreCase("s")) {
start(mSpannableStringBuilder, new Strikethrough());
} else if (tag.equalsIgnoreCase("strike")) {
start(mSpannableStringBuilder, new Strikethrough());
} 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') {
startHeading(mSpannableStringBuilder, attributes, tag.charAt(1) - '1');
} else if (tag.equalsIgnoreCase("img")) {
startImg(mSpannableStringBuilder, attributes, mImageGetter);
} else if (mTagHandler != null) {
//除以上标签以外,都回调给外部处理
mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader);
}
}
明明<SPAN> 是被解析的(tag.equalsIgnoreCase(“span”),就是没有颜色和粗体、斜体呢?再看startCssStyle(mSpannableStringBuilder, attributes)
startCssStyle(mSpannableStringBuilder, attributes)字体无效果实现
private void startCssStyle(Editable text, Attributes attributes) {
String style = attributes.getValue("", "style");
if (style != null) {
Matcher m = getForegroundColorPattern().matcher(style);
if (m.find()) {
int c = getHtmlColor(m.group(1));
if (c != -1) {
start(text, new Foreground(c | 0xFF000000));
}
}
m = getBackgroundColorPattern().matcher(style);
if (m.find()) {
int c = getHtmlColor(m.group(1));
if (c != -1) {
start(text, new Background(c | 0xFF000000));
}
}
m = getTextDecorationPattern().matcher(style);
if (m.find()) {
String textDecoration = m.group(1);
if (textDecoration.equalsIgnoreCase("line-through")) {
start(text, new Strikethrough());
}
}
}
}
取出style 属性,且此处指处理颜色,并没有处理字体,字体肯定是不会有效果的了。接着看getForegroundColorPattern。
getForegroundColorPattern颜色不显示的坑
再看取属性里面的颜色是通过正则表达式来取的,前景色正则表达式: “(?:s+|A)colors*:s*(S*)b”)
private static Pattern getForegroundColorPattern() {
if (sForegroundColorPattern == null) {
sForegroundColorPattern = Pattern.compile(
"(?:\s+|\A)color\s*:\s*(\S*)\b");
}
return sForegroundColorPattern;
}
这里是个坑,color是小写,内容中的是COLOR,没有匹配到颜色。同时背景色也是小写的color。
处理办法
调用还是不变,但要对原始内容进行修改
颜色修改
直接将style 属性的值修改为小写
<!--这样就可以显示颜色了-->
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy</SPAN>
粗体支持
从代码上看是明确不处理style 属性中的粗体,但从handleStartTag解析中有如下片段
else if (tag.equalsIgnoreCase("strong")) {
start(mSpannableStringBuilder, new Bold());
} else if (tag.equalsIgnoreCase("b")) {
start(mSpannableStringBuilder, new Bold());
}
基于这个片段,判断style中属性中的font-weight如果是bold值,那么直接在原内容基础上包裹标签<strong>或<b>。
具体如下:
<b>
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
</b>
//或
<strong>
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
</strong>
斜体支持
思路和粗体一样,选择多一点
代码片段:
else if (tag.equalsIgnoreCase("em")) {
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());
}
基于这个片段,判断style中属性中的font-style如果是italic值,那么直接在原内容基础上包裹标签<em>,<cite>,<dfn>,<i>之一
具体如下:
<em>
<b>
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
</b>
</em>
//或
<cite>
<b>
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
</b>
</cite>
//或
<dfn>
<b>
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
</b>
</dfn>
//或
<i>
<b>
<SPAN style="font-size: 10pt; font-weight: bold; color: #ff8000; font-style: italic">
hello<IMG src="emotionemotion.rb.gif" thePath custom="false">boy
</SPAN>
</b>
</i>
至此,这段html的颜色、粗体、斜体都能显示了。
最后
以上就是哭泣乐曲为你收集整理的Andoid TextView显示富文本html内容及问题处理富文本内容与效果TextView + HtmlImageGetter 处理图片(表情)TagHandler 处理html内容的节点Html的转换过程处理办法的全部内容,希望文章能够帮你解决Andoid TextView显示富文本html内容及问题处理富文本内容与效果TextView + HtmlImageGetter 处理图片(表情)TagHandler 处理html内容的节点Html的转换过程处理办法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复