有很多情况下,变态的需求,不得不让我们另辟蹊径,比如说,在一段文字里,愣是要让几处显示成别的颜色,或者字号要比其它文字大,或者小,其实这种情况下实现起来还算简单,只需要用到Html类的fromHtml()方法,加上html标签就可以实现,例如,把“AbnerMing是一个集才华与帅气于一身的帅哥”这句话中的AbnerMing设置成红色,字号稍大些,那么就可以这样做,Html.fromHtml(“<font color=‘#ff0000’ size=’14px’>AbnerMing</font>是一个集才华与帅气于一身的帅哥”),如果font不能实现可以改用<style='font-size: 14px'>,把它赋值给TextView上,就能显示了;显然这样处理一些文字上的需求是可以的,但是图片如何显示呢?
对于一个简单的文字处理,用以上的方式就能实现,但是,如果服务端返回的带img标签,那么用以上的方式图片就会显示不出来了,就比如下面的这段标签:
<font size='26px' color='#ff0000'>AbnerMing</font>是一个集才华与帅气于一身的帅哥,请关注我的微信公众号吧……<br/><img alt=‘微信公众号’ src='https://img-blog.csdn.net/20161230150153884?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWluZ18xNDc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast' style='width: 500px; height: 400px;'/>
如果想要显示出以上的效果,那么就需要自定义TextView了。
打开Html这个类,我们会看到一个方法:
/**
* Returns displayable styled text from the provided HTML string with the legacy flags
* {@link #FROM_HTML_MODE_LEGACY}.
*
* @deprecated use {@link #fromHtml(String, int, ImageGetter, TagHandler)} instead.
*/
@Deprecated
public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) {
return fromHtml(source, FROM_HTML_MODE_LEGACY, imageGetter, tagHandler);
}
虽然说这个方法已经过时,但是并不耽误我们进行使用,很显然这个方法的用意是返回html标签的应有的字符串,那么显而可见是支持返回img标签的。
我们首先自定义一个TextView类,继承TextView,实现构造方法,写一个方法,用来设置内容:
/**
* Parses String containing HTML to Android's Spannable format and displays
* it in this TextView.
*
* @param html String containing HTML, for example: "<b>Hello world!</b>"
*/
public void setHtmlFromString(String html) {
Html.ImageGetter imgGetter = new UrlImageGetter(this, getContext());
setText(Html.fromHtml(html, imgGetter, new HtmlTagHandler()));
setMovementMethod(LinkMovementMethod.getInstance());
}
这个方法就是我们用来设置内容的,和setText()方法的作用一样,html就是传入的文本,setText是为自己设置内容, setMovementMethod(LinkMovementMethod.getInstance()),是设置文本支持超链接作用,最重要的就是,Html.fromHtml(html, imgGetter, new HtmlTagHandler()),html是设置的文本信息,imgGetter,我们继续追溯Html这个类,发现它是一个可用于实现的接口,并提供了一个返回Drawable的方法。
/**
* 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);
}
这样就比较简单了,我们不妨继续写一个类,来实现Html.ImageGetter:
@Override
public Drawable getDrawable(String source) {
final UrlDrawable urlDrawable = new UrlDrawable();
ImageLoader.getInstance().loadImage(source, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
int imageWidth = loadedImage.getWidth();
int imageHeight = loadedImage.getHeight();
// 计算缩放比例
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
matrix.postScale(1.8f, 2.5f);
loadedImage = Bitmap.createBitmap(loadedImage, 0, 0, loadedImage.getWidth(), loadedImage.getHeight(), matrix,
true);
urlDrawable.bitmap = loadedImage;
urlDrawable.setBounds(0, 0, loadedImage.getWidth(), loadedImage.getHeight());
container.invalidate();
container.setText(container.getText()); // 解决图文重叠
}
});
return urlDrawable;
}
值得注意的是,这里我用到了ImageLoader,这个方法就是取得标签的scr路径,进行填充,返回一个Drawable,在这里我们可以设置,图片的大小,比例等。
new HtmlTagHandler()是自己定义的一个类,实现了Html类的TgHandler接口:
/**
* 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标签的,也是标签过滤器:
/**
* Created by xiaoming.li on 2017/1/2.
*/
public class HtmlTagHandler implements Html.TagHandler {
private int mListItemCount = 0;
private final Vector<String> mListParents = new Vector<String>();
private static class Code {
}
private static class Center {
}
@Override
public void handleTag(final boolean opening, final String tag, Editable output,
final XMLReader xmlReader) {
if (opening) {
// opening tag
if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol")
|| tag.equalsIgnoreCase("dd")) {
mListParents.add(tag);
mListItemCount = 0;
} else if (tag.equalsIgnoreCase("code")) {
start(output, new Code());
} else if (tag.equalsIgnoreCase("center")) {
start(output, new Center());
}
} else {
// closing tag
if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol")
|| tag.equalsIgnoreCase("dd")) {
mListParents.remove(tag);
mListItemCount = 0;
} else if (tag.equalsIgnoreCase("li")) {
handleListTag(output);
} else if (tag.equalsIgnoreCase("code")) {
end(output, Code.class, new TypefaceSpan("monospace"), false);
} else if (tag.equalsIgnoreCase("center")) {
end(output, Center.class,
new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), true);
}
}
}
/**
* Mark the opening tag by using private classes
*
* @param output
* @param mark
*/
private void start(Editable output, Object mark) {
int len = output.length();
output.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
}
private void end(Editable output, Class kind, Object repl, boolean paragraphStyle) {
Object obj = getLast(output, kind);
// start of the tag
int where = output.getSpanStart(obj);
// end of the tag
int len = output.length();
output.removeSpan(obj);
if (where != len) {
// paragraph styles like AlignmentSpan need to end with a new line!
if (paragraphStyle) {
output.append("\n");
len++;
}
output.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
/**
* Get last marked position of a specific tag kind (private class)
*
* @param text
* @param kind
* @return
*/
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for (int i = objs.length; i > 0; i--) {
if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
return objs[i - 1];
}
}
return null;
}
}
private void handleListTag(Editable output) {
if (mListParents.lastElement().equals("ul")) {
output.append("\n");
String[] split = output.toString().split("\n");
int lastIndex = split.length - 1;
int start = output.length() - split[lastIndex].length() - 1;
output.setSpan(new BulletSpan(15 * mListParents.size()), start, output.length(), 0);
} else if (mListParents.lastElement().equals("ol")) {
mListItemCount++;
output.append("\n");
String[] split = output.toString().split("\n");
int lastIndex = split.length - 1;
int start = output.length() - split[lastIndex].length() - 1;
output.insert(start, mListItemCount + ". ");
output.setSpan(new LeadingMarginSpan.Standard(15 * mListParents.size()), start,
output.length(), 0);
}
}
通过以上的代码,那么对于刚开始的那段文字,我们就可以展示出来了,具体显示会如下: