原文地址:http://blog.csdn.net/seker_xinjian/article/details/7236945
底部有源碼下載地址,不過需要收費(fèi),需要免費(fèi)的可以留言給我。
在開發(fā)Android應(yīng)用過程中,我曾遇到過下面的問題:
假設(shè)有一個(gè)View,它在做一系列復(fù)雜的、組合的Tween動(dòng)畫(平移動(dòng)畫、旋轉(zhuǎn)動(dòng)畫、縮放動(dòng)畫、Alpha動(dòng)畫)。在動(dòng)畫的過程中,用戶會(huì)去點(diǎn)擊這個(gè)View。如何去判斷這個(gè)View被點(diǎn)擊中了沒有呢?
為此,我曾專門在CSDN上發(fā)布了一條懸賞100分的技術(shù)貼:
http://topic.csdn.net/u/20111125/14/79debf30-c6ea-4945-ab1b-456e17259a2c.html。
以求得其解。然而,終究沒有得到相應(yīng)的答案。最終,得益于從前的兩位同事,找到了解決方案,特書此文,以供路人指教。
一、動(dòng)畫的原理
很多人看到我的帖子的時(shí)候,不懂我在說些什么,不知道問題點(diǎn)在哪里。他們可能覺得點(diǎn)擊事件只要注冊了“事件監(jiān)聽器”不久OK了么?
事實(shí)上不是這樣子的。正如我在上述的技術(shù)貼中提到的:
View做在做動(dòng)畫的時(shí)候,它并沒有真正的移動(dòng)它的位置。而是根據(jù)動(dòng)畫時(shí)間的插值,計(jì)算出一個(gè)Matrix,然后不停的invalidate,在onDraw中的Canvas上使用這個(gè)計(jì)算出來的Matrix去draw這個(gè)View的內(nèi)容。
換句話說,View在做動(dòng)畫的時(shí)候,它的位置根本沒有變化,只是畫它的時(shí)候進(jìn)行了Matrix處理,使得它看起來變化了。那么,動(dòng)畫中的View點(diǎn)擊事件的判斷區(qū)域,應(yīng)該是它“看起來”的那片區(qū)域,而不是它layout的那片區(qū)域。
我相信很多人還是不明白。所以特地找到了一個(gè)大牛人寫的另外一個(gè)博文《Android 動(dòng)畫框架詳解》,供大家搞明白Android中的動(dòng)畫原理。明白了Android補(bǔ)間動(dòng)畫的原理之后,然后再讀下去。
http://www.ibm.com/developerworks/cn/opensource/os-cn-android-anmt1/index.html
二、問題重述
比方說:一個(gè)矩形的View,它的的layout區(qū)域是(l,t,r,b),自然它的點(diǎn)擊事件的判斷區(qū)域也就是(l,t,r,b)
當(dāng)它做一個(gè)動(dòng)畫(平移動(dòng)畫、旋轉(zhuǎn)動(dòng)畫、縮放動(dòng)畫)時(shí),它的的layout區(qū)域依然是(l,t,r,b),但是它的顯示區(qū)域卻可是另外一片區(qū)域,比如是下圖(紅色區(qū)域):

這時(shí)候如果還是以(l,t,r,b)區(qū)域來點(diǎn)擊事件,自然就不可能正確了。
三、問題分析
《Android 動(dòng)畫框架詳解》所講的最核心的一點(diǎn)就是:Android 動(dòng)畫就是通過 ParentView 來不斷調(diào)整 ChildView 的畫布坐標(biāo)系來實(shí)現(xiàn)的。
嚴(yán)格來講,上述的“Android 動(dòng)畫”應(yīng)該限為:補(bǔ)間動(dòng)畫的1)、平移動(dòng)畫,2)、旋轉(zhuǎn)動(dòng)畫,3)、縮放動(dòng)畫。
動(dòng)畫的產(chǎn)生過程涉及到兩個(gè)重要的類型,Animation 和 Transformation,這兩個(gè)類是實(shí)現(xiàn)動(dòng)畫的主要的類。
Animation 中主要定義了動(dòng)畫的一些屬性比如開始時(shí)間、持續(xù)時(shí)間、是否重復(fù)播放等。這個(gè)類主要有兩個(gè)重要的函數(shù):getTransformation 和 applyTransformation,在 getTransformation 中 Animation 會(huì)根據(jù)動(dòng)畫的屬性來產(chǎn)生一系列的差值點(diǎn),然后將這些差值點(diǎn)傳給 applyTransformation,這個(gè)函數(shù)將根據(jù)這些點(diǎn)來生成不同的 Transformation。
Transformation 中包含一個(gè)矩陣和 alpha 值,矩陣是用來做平移、旋轉(zhuǎn)和縮放動(dòng)畫的,而 alpha 值是用來做 alpha 動(dòng)畫的(簡單理解的話,alpha 動(dòng)畫相當(dāng)于不斷變換透明度或顏色來實(shí)現(xiàn)動(dòng)畫)。這正好對應(yīng)著Transformation.TYPE_ALPHA和Transformation.TYPE_MATRIX這兩種類型。
四、解決方案
到此,可以看到如果一個(gè)View如果在做補(bǔ)間動(dòng)畫中的平移、旋轉(zhuǎn)、縮放動(dòng)畫,那么它的點(diǎn)擊事件一定要進(jìn)行的矩陣處理。
具體做法就是:
1、在ParentView中重寫onTouchEvent(MotionEvent event),攔截點(diǎn)擊點(diǎn)擊事件的x、y坐標(biāo)。注意(x,y)是相對于ParentView坐標(biāo)系的。
2、根據(jù)(x,y)坐標(biāo)算得“點(diǎn)擊”點(diǎn)相對于View坐標(biāo)系的坐標(biāo)點(diǎn)(x - view.getLeft(), y - view.getTop())。
3、獲得View的動(dòng)畫的時(shí)間,從而獲得Transformation,進(jìn)而獲得Matrix。然后求的Matrix的逆矩陣Matrix'。
4、使用Matrix'將坐標(biāo)點(diǎn)(x - view.getLeft(), y - view.getTop())求對應(yīng)的映射坐標(biāo)(x',y')。
5、(x',y')再還原成ParentView坐標(biāo)系中的點(diǎn)(x' + view.getLeft(), y' + view.getTop())。
6、使用View.getHitRect(rect),獲得“點(diǎn)擊判斷矩形”,再Rect.contains(int x, int y)判斷改點(diǎn)是否在View的區(qū)域范圍內(nèi)。
五、代碼
為了解決這個(gè)問題,我曾寫過一段測試代碼,也上傳到CSDN上來了。因?yàn)槭亲约涸瓌?chuàng),為了告慰為此而陣亡的腦細(xì)胞,因而該資源不是免費(fèi)的。
下載地址:http://download.csdn.net/detail/seker_xinjian/4047390