Android Lottie 踩坑之 LottieAnimationView 縮放無效

Lottie 作為一個(gè)可以輕松實(shí)現(xiàn)復(fù)雜動(dòng)畫的跨平臺(tái)開源動(dòng)畫庫闰挡,從發(fā)布至今翰铡,受到了越來越多開發(fā)者的推崇承粤。筆者所在項(xiàng)目從今年 8 月開始接入 lottie 庫揪漩,使用的是 2.0.0 的版本答捕。接入初期斯议,在享受 lottie 帶來的便利和高效同時(shí)沉填,有時(shí)會(huì)遇到動(dòng)畫效果和預(yù)期不符的情況有鹿,在修正了使用方法后吊奢,問題大多都能解決茉继。最近又遇到了一個(gè)問題咧叭,解決起來花費(fèi)了些精力,記錄下來烁竭。

問題描述

項(xiàng)目中有個(gè)“點(diǎn)贊”的動(dòng)效是用 lottie 實(shí)現(xiàn)的菲茬,而展示效果出現(xiàn)了偶現(xiàn)的 bug,LottieAnimationView 顯示過大:

"點(diǎn)贊" LottieAnimationView 顯示過大

開啟布局邊界可以清楚的看到“點(diǎn)贊” icon 顯示大小確實(shí)過大了派撕。該 icon 的布局代碼:

布局指定了 LottieAnimationView 的動(dòng)畫資源路徑婉弹、loop 和 scale 屬性,寬终吼、高為 wrap_content镀赌。

這里 scale 設(shè)置為 0.33 需要解釋一下。我們知道 lottie 的 JSON 格式的動(dòng)畫資源文件中是有動(dòng)畫的寬际跪、高屬性的商佛,并且 lottie-android 庫將 JSON 中的寬喉钢、高的單位視為 dp,也就是動(dòng)畫的實(shí)際顯示大小是 JSON 文件中設(shè)定的大小乘以手機(jī)系統(tǒng)的 density 值威彰。那么在 wrap_content 下出牧,為了使動(dòng)畫顯示大小在數(shù)值上和 JSON 中設(shè)定的一致,就需要指定一個(gè) scale 值歇盼,scale 值大小為 density 的倒數(shù)舔痕,以 density = 3 為例,這里 scale 即為 0.33豹缀。

但是從這個(gè) bug 來看伯复,動(dòng)畫 LottieAnimationView 并沒有按照預(yù)期的 scale 縮放,顯示的大小是未縮放的大小邢笙。

分析問題

View 大小顯示不正常啸如,應(yīng)該是 measure 過程出了問題。LottieAnimationView 自己沒有 onMeasure() 方法氮惯,于是查看一下其父類 ImageView 的 onMeasure() 方法叮雳。出現(xiàn) bug 的手機(jī)的系統(tǒng)是 Android 5.1 系統(tǒng),查看 Android 5.1 的 ImageView 源碼可以看到妇汗,onMeasure() 處理時(shí)是基于 mDrawableWidth 和 mDrawableHeight 來確定最終的大小帘不,因此猜測這兩個(gè)變量的賦值出現(xiàn)了問題。 mDrawableWidth 和 mDrawableHeight 的賦值在 updateDrawable 方法中杨箭,如下所示寞焙。

LottieDrawable 重寫了 getIntrinsicWidth() 和 getIntrinsicHeight() 方法

可以看到返回的結(jié)果已經(jīng)考慮 scale 值了。如果返回值不符合預(yù)期互婿,那么一定是 scale 值不正確捣郊,這里先留著,后面繼續(xù)分析慈参。

從 updateDrawable() 方法開始向上層層追蹤調(diào)用呛牲,可以找到調(diào)用 updateDrawable() 方法的調(diào)用鏈:


updateDrawable() 方法的調(diào)用關(guān)系

由此可知,LottieAnimationView 在解析動(dòng)畫文件成 LottieComposition 后懂牧,setComposition() 時(shí)會(huì)調(diào)用到 updateDrawable() 來獲取 drawable 的大小侈净,進(jìn)而確定自身的大小。

由于我們把動(dòng)畫資源寫到了 xml 布局文件中僧凤,所以 LottieComposition 的解析時(shí)機(jī)是在 LottieAnimationView 的 init() 方法中畜侦。

setAnimation() 方法首先在緩存池中查找是否存在解析好的相同文件名的動(dòng)畫文件,如果存在直接調(diào)用 setComposition() 使用躯保;如果不存在旋膳,則啟動(dòng) loader 加載、解析動(dòng)畫文件途事,在回調(diào)函數(shù)中調(diào)用 setComposition()验懊,相關(guān)代碼如下:

到這里擅羞,代碼邏輯已經(jīng)梳理得比較清晰了:在 LottieAnimationView 中解析動(dòng)畫文件成 LottieComposition,然后調(diào)用 setComposition() 保存义图、處理 LottieComposition减俏,最終調(diào)用到 ImageView 的 updateDrawable() 方法,獲取 LottieDrawable 的尺寸碱工,反映到 LottieAnimationView 上娃承。

這個(gè)流程看起來沒什么問題。但是從 bug 上來看怕篷,最終獲取的 LottieDrawable 尺寸是錯(cuò)誤的历筝。根據(jù)前面的結(jié)論,此時(shí)應(yīng)該是 scale 值不正確了廊谓。繼續(xù)看一下 scale 設(shè)置的時(shí)機(jī)梳猪。

從上述 LottieAnimationView 的 init() 方法中看到,scale 值從 xml 布局文件中解析得來蒸痹,數(shù)據(jù)肯定不會(huì)有錯(cuò)誤春弥。但是設(shè)置的時(shí)機(jī)是在 setAnimation() 之后。這會(huì)有什么問題呢叠荠?

當(dāng)然有問題了惕稻,setAnimation() 時(shí)如果緩存中有解析好的動(dòng)畫資源,那么就會(huì)直接獲取使用蝙叛,繼續(xù)執(zhí)行到 ImageView.updateDrawable(),此時(shí) scale 值還未設(shè)置公给,初始為 1f借帘,所以獲取到的 LottieDrawable 大小就是未縮放的大小了,LottieAnimationView 的大小也就偏大了淌铐。

那本文開頭提到的 bug 原因是這樣的嗎肺然?答案是肯定的。這個(gè)“點(diǎn)贊”動(dòng)畫資源在其他的業(yè)務(wù)場景中也有使用腿准,并且际起,由于是在列表中使用,因此做了強(qiáng)緩存設(shè)置吐葱。所以只要這個(gè)“點(diǎn)贊”資源加載街望、解析過,那么就會(huì)緩存下來弟跑,進(jìn)入到其他頁面再次使用時(shí)灾前,此 bug 就會(huì)復(fù)現(xiàn)。

問題原因找到了孟辑,趕緊修復(fù)吧哎甲。等等蔫敲,筆者發(fā)現(xiàn)按照上面提到的復(fù)現(xiàn)路徑,在 Android 6.0 以上系統(tǒng)上并沒有出現(xiàn)這個(gè) bug炭玫,這是怎么回事呢奈嘿?

查看 ImageView 的源碼發(fā)現(xiàn),Android 6.0 以上系統(tǒng)里吞加,ImageView 中 mDrawableWidth 和 mDrawableHeight 多了一個(gè)賦值時(shí)機(jī)裙犹,而這個(gè)時(shí)機(jī)是在 Android 5.x 系統(tǒng)里沒有的。

對(duì)比 Android 6.0+ 和 Android 5.x 的 ImageView 的 invalidateDrawable() 可知榴鼎,Android 6.0+ 系統(tǒng)上會(huì)根據(jù)獲取到的 drawable 大小來更新mDrawableWidth伯诬、mDrawableHeight 的值。

這就不難解釋為什么 Android 6.0 以上系統(tǒng)不會(huì)出現(xiàn)這個(gè) bug 了巫财。LottieDrawable 繪制時(shí)調(diào)用自己的 invalidateSelf()盗似,invalidateSelf() 方法會(huì)調(diào)用到 ImageView 的 invalidateDrawable(),此時(shí) scale 值已經(jīng)設(shè)置完畢平项,就可以保證獲取到的 LottieDrawable 的大小是正確的了赫舒。

根據(jù)以上分析,LottieAnimationView 縮放無效的 bug 出現(xiàn)在 Android 5.x 系統(tǒng)上闽瓢,由于縮放參數(shù) scale 設(shè)置時(shí)機(jī)在動(dòng)畫解析之后接癌,所以緩存中有動(dòng)畫資源時(shí),還沒等到 scale 設(shè)置好扣讼,就直接獲取 drawable 的大小作為 LottieAnimationView onMeasure() 參考的大小了缺猛。

解決問題

問題原因找到了,就好辦了椭符。

首先考慮是否可以修正 lottie 的使用姿勢來避免這個(gè)問題呢荔燎?比如在 java 代碼中通過 setAnimation() 來設(shè)置動(dòng)畫文件,而不是在 xml 布局文件中設(shè)置销钝。這樣就能夠確保 setAnimation() 的調(diào)用在設(shè)置 scale 之后了有咨。這種方式理論上是可以解決問題的,但是實(shí)際操作上是不可行的蒸健。因?yàn)檫@樣做相當(dāng)于設(shè)定了一個(gè)使用 lottie 開發(fā)的規(guī)則座享,削減了 lottie 開發(fā)的便利性的同時(shí),讓團(tuán)隊(duì)每個(gè)人都遵守起來成本很高似忧,而且難以保證不會(huì)出錯(cuò)渣叛。

那么就只能通過修改 lottie 源碼來解決了。這樣做也是合理的盯捌,因?yàn)楂@取 drawable 尺寸時(shí)依賴于 scale 的值诗箍,邏輯上,此時(shí) scale 值必須設(shè)置完畢才行。因此滤祖,我們將 LottieAnimationView 的 init() 方法修改了一下筷狼,將 scale 值的設(shè)置提前。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匠童,一起剝皮案震驚了整個(gè)濱河市埂材,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汤求,老刑警劉巖俏险,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扬绪,居然都是意外死亡竖独,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門挤牛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莹痢,“玉大人,你說我怎么就攤上這事墓赴【荷牛” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵诫硕,是天一觀的道長坦辟。 經(jīng)常有香客問我,道長章办,這世上最難降的妖魔是什么锉走? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮藕届,結(jié)果婚禮上挠日,老公的妹妹穿的比我還像新娘。我一直安慰自己翰舌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布冬骚。 她就那樣靜靜地躺著椅贱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪只冻。 梳的紋絲不亂的頭發(fā)上庇麦,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音喜德,去河邊找鬼山橄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舍悯,可吹牛的內(nèi)容都是我干的航棱。 我是一名探鬼主播睡雇,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼饮醇!你這毒婦竟也來了它抱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤朴艰,失蹤者是張志新(化名)和其女友劉穎观蓄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祠墅,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侮穿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毁嗦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亲茅。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖金矛,靈堂內(nèi)的尸體忽然破棺而出芯急,到底是詐尸還是另有隱情,我是刑警寧澤驶俊,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布娶耍,位于F島的核電站,受9級(jí)特大地震影響饼酿,放射性物質(zhì)發(fā)生泄漏榕酒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一故俐、第九天 我趴在偏房一處隱蔽的房頂上張望想鹰。 院中可真熱鬧,春花似錦药版、人聲如沸辑舷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽何缓。三九已至,卻和暖如春还栓,著一層夾襖步出監(jiān)牢的瞬間碌廓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工剩盒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谷婆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像纪挎,于是被迫代替她去往敵國和親期贫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容