#Android笔记# 超级足球app 开发总结(二)—— SpannableString根据标记插入文字实现图文混排

最近利用业余时间,开发了一款基于懂球帝接口数据的足球资讯app,整体的UI也是仿照懂球帝设计的。这是一个比较综合的项目,用到了不少以前没用过的组件和api,而且产生了很多新的开发思路,有些实现方式也是自己琢磨的,所以值得做一些记录,可能还存在瑕疵和可以优化的地方,也希望大家给我多指正。

先来看看实现前后的对比图:

#Android笔记# 超级足球app 开发总结(二)—— SpannableString根据标记插入文字实现图文混排_第1张图片

#Android笔记# 超级足球app 开发总结(二)—— SpannableString根据标记插入文字实现图文混排_第2张图片

再来看一看接口返回的数据(数据结构比较长,这里只截取了部分用到的数据):

#Android笔记# 超级足球app 开发总结(二)—— SpannableString根据标记插入文字实现图文混排_第3张图片

#Android笔记# 超级足球app 开发总结(二)—— SpannableString根据标记插入文字实现图文混排_第4张图片

可以看到,懂球帝这里是通过file_name去跟文本中对应的标记匹配来实现图文混排的,而不是通过html格式去做的,因此我这里想到的是通过在spannableString中插入图片的方式去实现混排。

思路整理

1、通过glide将图片下载下来;

Glide.with(context).load(a.getUrl()).into(new SimpleTarget() {
    @Override
    public void onResourceReady(@NonNull Drawable resource, @Nullable Transition 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() {
                            @Override
                            public void onResourceReady(@NonNull Drawable resource, @Nullable Transition 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样式的话,就相对复杂了。实现这种图文混排的方式应该还有很多,如果你耐心看到这里,不妨留下你的一些想法吧,我们可以一起交流,共同进步!

你可能感兴趣的:(Android笔记)