前言
利用ImageSpan實(shí)現(xiàn)TextView的圖文混排的教程數(shù)不勝數(shù)悦施,但是當(dāng)?shù)腎mageSpan中的drawable是一張動(dòng)圖的時(shí)候,卻往往發(fā)現(xiàn)這張圖并沒有像預(yù)期一樣動(dòng)起來笆环。
我在做聊天功能的時(shí)候也碰到了這個(gè)問題,網(wǎng)上搜索半天,大體的思路倒是有了啼器,不過并沒有發(fā)現(xiàn)一個(gè)較為全面和完整的總結(jié)及代碼實(shí)現(xiàn)。
于是自己寫了一個(gè)使用極其簡(jiǎn)單的方法并分析總結(jié)一下實(shí)現(xiàn)的思路以及實(shí)現(xiàn)過程中碰到的坑俱萍,好讓要做同樣功能的同學(xué)少走彎路端壳。
ScreenShot
解決方案
先上解決方案,有興趣的可以繼續(xù)往下看我的分析
項(xiàng)目地址 https://github.com/sunhapper/SpEditTool
用法說明 SpEditTool使用指南
歡迎star枪蘑,提PR损谦、issue
- 先引入上面的庫SpEditTool
- 使用其中
GifTextUtil.setText(TextView textView,CharSequence text)
代替TextView的setText
GifTextUtil.setText
方法會(huì)將TextView和包含動(dòng)圖的Spannable字符串綁定并在動(dòng)圖刷新時(shí)刷新TextView以實(shí)現(xiàn)gif的圖文混排
重要說明
- 這段代碼功能只是幫助有g(shù)if圖文混排的TextView設(shè)置回調(diào)使其動(dòng)起來,至于如何利用Glide或者GifDrawable產(chǎn)生一個(gè)可以動(dòng)的drawable,請(qǐng)看一行代碼讓TextView中ImageSpan支持Gif(三)
- 個(gè)人能力有限,一行代碼實(shí)現(xiàn)不了drawable復(fù)用場(chǎng)景(RecyclerView,ListView及多個(gè)TextView使用同一個(gè)Drawable對(duì)象)下的TextView刷新,但解決方案會(huì)在一行代碼讓TextView中ImageSpan支持Gif(四)中給出,只需要多實(shí)現(xiàn)一個(gè)接口,有需要的可以看看
- 只對(duì)ImageSpan及其子類添加回調(diào)
原理分析
動(dòng)圖不刷新的原因
動(dòng)圖只是一個(gè)drawable,并不知道是哪個(gè)View持有了它岳颇,所以drawable只能刷新它自己照捡,而外部的View沒有刷新的話顯示在屏幕上的還是drawable刷新之前的樣子。
解決方法
- 最初寫這個(gè)功能的時(shí)候话侧,項(xiàng)目時(shí)間緊栗精,所以直接寫了一個(gè)每隔100ms自己刷新一次的自定義TextView,這種方式稍顯粗暴瞻鹏,如果文本內(nèi)容不包括gif的話TextView還是會(huì)自己刷新悲立,浪費(fèi)CPU資源
- 好一些的方法,drawable可以設(shè)置一個(gè)
Drawable.Callback
,drawable刷新自己的時(shí)候會(huì)回調(diào)invalidateDrawable
,可以在這個(gè)方法中刷新TextView
一個(gè)不完善的思路
最初的思路來源于android平臺(tái)TextView使用ImageSpan展示GIF圖片 ,雖然最終實(shí)現(xiàn)的時(shí)候發(fā)現(xiàn)這篇文章的代碼大部分是紙上談兵,不能真正完成功能,不過思路還是給了我很大的啟示,對(duì)作者表示感謝
簡(jiǎn)單總結(jié)下這篇文章的思路
- 繼承TextView/EditText
- 覆蓋setText方法,對(duì)新設(shè)置的文本,遍歷其中的ImageSpan,對(duì)其中的drawable設(shè)置刷新回調(diào)
- 在setText方法中設(shè)置SpanWatcher,監(jiān)聽I(yíng)mageSpan的移除,在ImageSpan移除時(shí)移除其中drawable的回調(diào)
- 在drawable刷新回調(diào)中使用removeSpan,setSpan去刷新TextView
錯(cuò)誤分析
- 文章中在super.setText之前設(shè)置SpanWatcher,然而SpanWatcher繼承自
NoCopySpan
,在setText時(shí)不會(huì)被復(fù)制到TextView中的Spannale對(duì)象中,所以設(shè)置永遠(yuǎn)不會(huì)成功. -
removeSpan
是個(gè)耗時(shí)操作,文章中在setText和刷新時(shí)都會(huì)調(diào)用多次removeSpan,插入的ImageSpan一多就會(huì)非承虏卡
改進(jìn)
- 不使用繼承,因?yàn)閖ava的單繼承,所以個(gè)人建議除非必要不在要給別人使用的代碼中使用繼承
- 在setText之后,取出TextView的Spannable再對(duì)其設(shè)置
SpanWatcher
,保證設(shè)置成功 - 取消已經(jīng)被移除的drawable的監(jiān)聽不使用removeSpan,而只是將監(jiān)聽設(shè)置disable,減少耗時(shí)
- 直接刷新TextView,而不是先removeSpan再setSpan
總結(jié)
讓TextView中的gif動(dòng)起來,一句話總結(jié)就是先設(shè)置drawable的Drawable.Callback
,然后在invalidateDrawable
回調(diào)中刷新TextView,使用GifTextUtil.setText
一行代碼就可以實(shí)現(xiàn)這一功能了
具體如何實(shí)現(xiàn)請(qǐng)看一行代碼讓TextView中ImageSpan支持Gif(二)
索引
一行代碼讓TextView中ImageSpan支持Gif(一)
第一篇給出解決方案并分析整體思路
一行代碼讓TextView中ImageSpan支持Gif(二)
第二篇對(duì)實(shí)現(xiàn)中的細(xì)節(jié)和踩過的坑進(jìn)行說明
一行代碼讓TextView中ImageSpan支持Gif(三)
第三篇介紹如何使用android-gif-drawable和Glide實(shí)現(xiàn)遠(yuǎn)程gif圖片在TextView中的圖文混排
一行代碼讓TextView中ImageSpan支持Gif(四)
第四篇介紹在RecyclerView等需要drawable復(fù)用的場(chǎng)景下的gif動(dòng)圖顯示