android隨筆之為什么屬性動(dòng)畫移動(dòng)一個(gè)控件后,目標(biāo)位置仍然能響應(yīng)用戶事件?補(bǔ)間動(dòng)畫就不行呢?

如題,多的不說,少的不嘮,直接入主題:

為什么屬性動(dòng)畫移動(dòng)一個(gè)控件后,目標(biāo)位置仍然能響應(yīng)用戶事件?

也就是說,應(yīng)用了屬性動(dòng)畫之后,該View依然可以正確地接收到事件的分派。
那就要搞清楚ViewGroup它是怎么找到這個(gè)"偷跑"了的View的。
我們知道,調(diào)用View的translationXX方法之后,雖然在屏幕上的位置是變了,但是它的[left,top,right,bottom]是不會(huì)變的。

來捋一遍ViewGroup分派事件的大致流程:

當(dāng)手指按下時(shí),觸摸事件會(huì)經(jīng)過ViewGroup中的dispatchTouchEvent方法篩選符合條件(手指在邊界范圍內(nèi))的子View進(jìn)行分派事件(如果未被onInterceptTouchEvent攔截的話)。
那么,如果某個(gè)子View剛好應(yīng)用了translation屬性動(dòng)畫,在ViewGroup篩選子View時(shí),直接判斷觸摸點(diǎn)是否在[left,top,right,bottom]范圍內(nèi),是肯定不行的。

那它是怎么判斷的呢?

它會(huì)先調(diào)用子View的hasIdentityMatrix方法來判斷這個(gè)View是否應(yīng)用過位移、縮放、旋轉(zhuǎn)之類的屬性動(dòng)畫。
如果應(yīng)用過的話,那接下來還會(huì)把觸摸點(diǎn)映射到該子View的逆矩陣上(getInverseMatrix)。
判斷處理后的觸摸點(diǎn),是否在該子View的邊界范圍內(nèi)。
上面說到了"把觸摸點(diǎn)映射到該子View的逆矩陣上",那它是怎么個(gè)映射法:
比如一個(gè)View它水平平移了200,那它所對(duì)應(yīng)的逆矩陣就是水平平移了-200,
如果觸摸點(diǎn)坐標(biāo)是[500,500]的話,那么映射之后,就是[300,500],也就是反方向移動(dòng)同樣的距離了。

可以這樣來理解:

如果一個(gè)View向右移動(dòng)了一個(gè)拇指的距離,當(dāng)手指在它的新位置上按下的時(shí)候,
(它最終還是要判斷是否在原來的邊界范圍內(nèi)的,那只能把觸摸的坐標(biāo),給轉(zhuǎn)回去,轉(zhuǎn)回它應(yīng)用變換之前的位置上),
那ViewGroup在檢測到它應(yīng)用了變換后,會(huì)把現(xiàn)在的觸摸點(diǎn),向左(剛剛是向右)移動(dòng)一個(gè)拇指的距離(抵消),再來判斷是否在該View的邊界范圍內(nèi)。
那么為什么只有屬性動(dòng)畫可以這樣,補(bǔ)間動(dòng)畫就不行呢?
View在draw的時(shí)候,會(huì)檢測是否設(shè)置了Animation(補(bǔ)間動(dòng)畫),
如果有的話,會(huì)獲取這個(gè)動(dòng)畫當(dāng)前的值(旋轉(zhuǎn)或位移或縮放,透明度等),應(yīng)用到canvas上,然后把東西draw出來。
比如設(shè)置了位移動(dòng)畫,當(dāng)前值是向右移動(dòng)了100,那么效果就等于這樣:
Matrix matrix = new Matrix();
matrix.setTranslate(100, 0);
canvas.setMatrix(matrix);
它的作用只會(huì)在draw的時(shí)候有效。
雖然大家都是操作Matrix,但是Matrix的對(duì)象不一樣(屬性動(dòng)畫操作的Matrix,是View的mRenderNode所對(duì)應(yīng)的Matrix),
所以在ViewGroup篩選的時(shí)候,應(yīng)用屬性動(dòng)畫的View會(huì)被正確找到,而補(bǔ)間動(dòng)畫的不行。
屬性動(dòng)畫所影響的Matrix,是在View的mRenderNode中的stagingProperties里面的,這里的Matrix,每個(gè)View之間都是獨(dú)立的,所以可以各自保存不同的變換狀態(tài)。
而補(bǔ)間動(dòng)畫,它所操作的Matrix,其實(shí)是借用了它父容器的一個(gè)叫mChildTransformation的屬性(里面有Matrix),通過getChildTransformation獲得。
也就是說,一個(gè)ViewGroup中,無論它有幾個(gè)子View都好,在這些子View播放補(bǔ)間動(dòng)畫的時(shí)候,都是共用同一個(gè)Transformation對(duì)象的(也就是共用一個(gè)Matrix),這個(gè)對(duì)象放在ViewGroup里面。
有同學(xué)可能會(huì)問:共用?不可能吧,那為什么可以同時(shí)播放好幾個(gè)動(dòng)畫,而互相不受影響呢?
是的,在補(bǔ)間動(dòng)畫更新每一幀的時(shí)候,父容器的mChildTransformation里面的Matrix,都會(huì)被reset。

每次重置Matrix而不受影響的原因:

是因?yàn)檫@些補(bǔ)間動(dòng)畫,都是基于當(dāng)前播放進(jìn)度,來計(jì)算出絕對(duì)的動(dòng)畫值并應(yīng)用的,保存舊動(dòng)畫值是沒有意義的。
就拿位移動(dòng)畫TranslateAnimation來說,比如它要向右移動(dòng)500,當(dāng)前的播放進(jìn)度是50%,那就是已經(jīng)向右移動(dòng)了250,在View更新幀的時(shí)候,就會(huì)把這個(gè)向右移動(dòng)了250的Matrix應(yīng)用到Canvas上,當(dāng)下次更新幀時(shí),比如進(jìn)度是60%,那計(jì)算出來的偏移量就是300,這時(shí)候,已經(jīng)不需要上一次的舊值250了,就算Matrix在應(yīng)用前被重置了,也不影響最后的效果。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容