让TextView中ImageSpan支持Gif,轻松实现TextView中gif图文混排

李烨
2023-12-01

利用ImageSpan实现TextView的图文混排的教程数不胜数,但是当的ImageSpan中的drawable是一张动图的时候,却往往发现这张图并没有像预期一样动起来。
我在做聊天功能的时候也碰到了这个问题,网上搜索半天,大体的思路倒是有了,不过并没有发现一个较为全面和完整的总结及代码实现。
于是自己写了一个使用极其简单的方法并分析总结一下实现的思路以及实现过程中碰到的坑,好让要做同样功能的同学少走弯路。
解决方案
先上解决方案,有兴趣的可以继续往下看我的分析

项目地址 https://github.com/sunhapper/SpEditTool
用法说明 SpEditTool使用指南
欢迎star,提PR、issue

先引入上面的库SpEditTool
使用其中GifTextUtil.setText(TextView textView,CharSequence text)代替TextView的setText
GifTextUtil.setText方法会将TextView和包含动图的Spannable字符串绑定并在动图刷新时刷新TextView以实现gif的图文混排

重要说明
这段代码功能只是帮助有gif图文混排的TextView设置回调使其动起来,至于如何利用Glide或者GifDrawable产生一个可以动的drawable,请看一行代码让TextView中ImageSpan支持Gif(三)
个人能力有限,一行代码实现不了drawable复用场景(RecyclerView,ListView及多个TextView使用同一个Drawable对象)下的TextView刷新,但解决方案会在一行代码让TextView中ImageSpan支持Gif(四)中给出,只需要多实现一个接口,有需要的可以看看
只对ImageSpan及其子类添加回调
原理分析
动图不刷新的原因
动图只是一个drawable,并不知道是哪个View持有了它,所以drawable只能刷新它自己,而外部的View没有刷新的话显示在屏幕上的还是drawable刷新之前的样子。

解决方法
最初写这个功能的时候,项目时间紧,所以直接写了一个每隔100ms自己刷新一次的自定义TextView,这种方式稍显粗暴,如果文本内容不包括gif的话TextView还是会自己刷新,浪费CPU资源
好一些的方法,drawable可以设置一个Drawable.Callback,drawable刷新自己的时候会回调invalidateDrawable,可以在这个方法中刷新TextView
一个不完善的思路
最初的思路来源于android平台TextView使用ImageSpan展示GIF图片 ,虽然最终实现的时候发现这篇文章的代码大部分是纸上谈兵,不能真正完成功能,不过思路还是给了我很大的启示,对作者表示感谢

简单总结下这篇文章的思路

继承TextView/EditText
覆盖setText方法,对新设置的文本,遍历其中的ImageSpan,对其中的drawable设置刷新回调
在setText方法中设置SpanWatcher,监听ImageSpan的移除,在ImageSpan移除时移除其中drawable的回调
在drawable刷新回调中使用removeSpan,setSpan去刷新TextView
错误分析
文章中在super.setText之前设置SpanWatcher,然而SpanWatcher继承自NoCopySpan,在setText时不会被复制到TextView中的Spannale对象中,所以设置永远不会成功.
removeSpan是个耗时操作,文章中在setText和刷新时都会调用多次removeSpan,插入的ImageSpan一多就会非常卡
改进
不使用继承,因为java的单继承,所以个人建议除非必要不在要给别人使用的代码中使用继承
在setText之后,取出TextView的Spannable再对其设置SpanWatcher,保证设置成功
取消已经被移除的drawable的监听不使用removeSpan,而只是将监听设置disable,减少耗时
直接刷新TextView,而不是先removeSpan再setSpan
总结
让TextView中的gif动起来,一句话总结就是先设置drawable的Drawable.Callback,然后在invalidateDrawable回调中刷新TextView,使用GifTextUtil.setText一行代码就可以实现这一功能了
具体如何实现请看一行代码让TextView中ImageSpan支持Gif(二)

原文链接:https://www.jianshu.com/p/3ae513115c17

 类似资料: