最近利用业余时间,开发了一款基于懂球帝接口数据的足球资讯app,整体的UI也是仿照懂球帝设计的。这是一个比较综合的项目,用到了不少以前没用过的组件和api,而且产生了很多新的开发思路,有些实现方式也是自己琢磨的,所以值得做一些记录,可能还存在瑕疵和可以优化的地方,也希望大家给我多指正。
先来看看实现前后的对比图:
再来看一看接口返回的数据(数据结构比较长,这里只截取了部分用到的数据):
可以看到,懂球帝这里是通过file_name去跟文本中对应的标记匹配来实现图文混排的,而不是通过html格式去做的,因此我这里想到的是通过在spannableString中插入图片的方式去实现混排。
思路整理
1、通过glide将图片下载下来;
Glide.with(context).load(a.getUrl()).into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
}
});
2、利用String的indexOf函数,通过匹配file_name,找到图片在文本中的位置;
int startIndex = circle.getContent().indexOf(a.getFile_name());
int endIndex = startIndex+a.getFile_name().length();
3、利用spannableString的setSpan函数,将图片插入到对应位置;
spannableString.setSpan(imageSpan, startIndex,endIndex, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
遇到的问题
1、下载图片是需要通过异步方式去实现的,那么我就没法控制 当所有图片都下载完成后再通知TextView更新文本了。
解决办法:后来发现spannableString的setSpan()并不会覆盖上一次的样式,而是类似于addSpan的效果,因此我的解决方法就是每加载完一张图片,就setSpan()一次,更新一次样式;
spannableString.setSpan(imageSpan, startIndex,endIndex, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
handler.sendEmptyMessage(i);
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
tvContent.setText(spannableString);
}
};
2、setSpan之后,文本的{{p1}}、{{p2}}等处确实不显示了,但图片未加载到对应位置。
解决办法:出现这种问题的原因是没有对图片的宽高进行设置,需要调用drawable的setBounds()设置图片的大小。
//设置图片显示的宽高
resource.setBounds(0,0,(int)picWidth,(int)picHeight);
3、图片与文字之间的行高与文件间的行高不一致;
解决办法:首先去除文本中多余的空行
public static String deleteCRLF(String input) {
return input.replaceAll("((\r\n)|\n)[\\s\t ]*(\\1)+", "$1").replaceAll("^((\r\n)|\n)", "");
}
然后对ImageSpan做一些处理(这个是我直接上网百度的,原理有待研究)
public class MyImageSpan extends ImageSpan {
public MyImageSpan( Drawable b) {
super(b);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
Drawable b = getDrawable();
canvas.save();
int transY;
//要显示的文本高度-图片高度除2等居中位置+top(换行情况)
transY = ((bottom - top) - b.getBounds().bottom) / 2 + top;
//偏移画布后开始绘制
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
}
最终实现
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
tvContent.setText(spannableString);
}
};
for(CircleNews.Attachment a: circle.getAttachments()){
Glide.with(context).load(a.getUrl()).into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
MyImageSpan imageSpan = new MyImageSpan(resource);
//ImageSpan imageSpan = new ImageSpan(resource);
double picWidth = UIUtils.getScreenWidth(context);
double ratio = picWidth/(double)resource.getIntrinsicWidth();
double picHeight = ratio*resource.getIntrinsicHeight();
//设置图片显示的宽高
resource.setBounds(0,0,(int)picWidth,(int)picHeight);
int i = circle.getAttachments().indexOf(a);
int startIndex = circle.getContent().indexOf(a.getFile_name());
int endIndex = startIndex+a.getFile_name().length();
spannableString.setSpan(imageSpan, startIndex,
endIndex, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
handler.sendEmptyMessage(i);
}
});
}
总结
之前处理富文本通常是通过html样式的去实现的;通过研究懂球帝的这个api接口,又获得了一种新的思路:可以通过SpannableString+文本标记的方式去实现富文本,使用SpannableString的好处是可以实现高度的自定义,比如说插入一个自定义表情,使用SpannableString只需在文本中增加一个标记识别即可,而使用html样式的话,就相对复杂了。实现这种图文混排的方式应该还有很多,如果你耐心看到这里,不妨留下你的一些想法吧,我们可以一起交流,共同进步!