Android Touch事件的分發過程

一.不知道你是否在涉及到Android觸屏事件的時候有過以下的疑問:html

1.View的onTouchEvent()方法返回true和false有什麼區別? SDK給出的解釋很簡單:"返回true表明該事件已經被處理過了,返回false則相反",這句話徹底沒有解釋清楚問題。android

2.View的onTouchEvent()方法在處理ACTION_DOWN的時候返回true,在處理ACTION_MOVE的時候返回false,表明着是處理了仍是沒處理?返回super.onTouchEvent()又是什麼含義?佈局

3.重寫onTouchEvent()方法和經過setOnTouchListener()設置一個觸屏監聽有什麼區別,看起來好像很相似。this

4.View的dispatchTouchEvent(),onTouchEvent(),setOnClickListener(),ViewGroup的onInterceptTouchEvent()把我繞暈了,這些方法怎麼使用怎麼重寫?spa

5.假設一個ViewGroup有兩個子view,這兩個view有一部分是重疊的,點擊該重疊部分,事件由哪一個View來處理?htm

6.最重要的一點疑問是:觸屏事件從頂層ViewGroup一直向下是怎麼傳遞的?繼承

若是你有相似的疑問,相信個人這篇博客能給你答案。遞歸

二.首先須要明確的幾點是:進程

1.View通常是爲了顯示某些內容而存在的,它也一般用來處理用戶的觸屏等交互事件,而ViewGroup則是作爲View的容器而存在的,雖然在代碼上它是View的子類,但它一般只是作爲容器用來組織它的子視圖佈局方式。事件

2.咱們知道android裏邊View層次是一種樹型結構,須要明確的是一個ViewGroup它的直接子視圖纔算是樹結構中的兒子,再往下一層就不算了,相似於進程間的父子關係。舉例,FrameLayout有兩個子視圖,分別是LinearLayout和TextView,而 LinearLayout又有三個子視圖ImageView,那麼調用FrameLayout的getChildCount()方法只會返回2,而不是 5。所以如下內容中"子視圖"這個術語表明着一個ViewGroup的直接子視圖,它便可能是一個View類,也多是一個ViewGroup類。

3.Activity視圖的最頂層View是DecorView,它是在PhoneWindow類中經過generateDecor()方法生成的,它繼承自FrameLayout,是View層次的根視圖。

4.對於觸屏來講有三個主要的事件:down,move,up

那麼一個觸屏事件究竟是怎麼在View層次中上向下傳遞的?(這裏只考慮事件已經到達DecorView時的情形,事實上是ViewRootImpl 類接收到底層InputDispatch傳遞過來的事件,這裏就不寫了),ViewRootImpl在deliverPointerEvent()方法中經過調用mView.dispatchPointerEvent(event);將觸屏事件傳遞給了DecorView,DecorView經過 dispatchTouchEvent()繼續向下傳遞給子視圖,若是子視圖也是一個ViewGroup,它又會調用本身的 dispatchTouchEvent()方法向下傳遞,若是子視圖是一個View,那麼子視圖的onTouchEvent()方法就會被調用,若是子視圖處理了該事件,那麼事件傳遞就停止。整個過程像是一個遞歸過程,理解了一個ViewGroup怎麼經過dispatchTouchEvent()傳遞給它的子視圖這一層也就理解了整個過程。

這裏就不分析ViewGroup的dispatchTouchEvent()方法的代碼了,直接給出我總結出來的結論,有興趣的讀者能夠分析看看。

三.總結

如下情景假設一個ViewGroup有三個子視圖,按index順序爲v1,v2,v3。v1也是一個ViewGroup,v2和v3都是普通的view,並且它們有一點重疊的部分。

1.ViewGroup的dispatchTouchEvent()向下分發事件給它的子視圖,那麼會先分發給v3調用它的onTouchEvent 方法,若是v3不處理該事件,會繼續分發給v2,若是v2不處理事件,會繼續分發給v1,因爲v1是一個ViewGroup,則會調用它的 dispatchTouchEvent()分發給它的子視圖。

2.v3不處理該事件的含義是:在down事件到達時,onTouchEvent()方法返回false,若是在接收到down事件時返回true, 則表示處理了該事件,那麼無論你在接收到move和up事件的時候返回的是什麼都沒有關係。所以思想是:只要你願意處理down事件,那麼你必須處理接下來的其餘事件。

3.v3能接收觸屏事件的前提是它的顯示矩形框必須在觸屏的範圍以內,這裏顯而易見的道理,不然事件會傳遞給v2。

4.若是v1,v2,v3都決定不處理觸屏事件,那麼事件最終由ViewGroup本身來處理,它的onTouchEvent()方法會被調用。

5.若是事件傳遞到了v1,v1是否處理取決於它的子視圖,若是它的子視圖有一個處理了該事件,那麼就表明v1處理了事件,若是它的全部子視圖都沒有處理事件並且v1自己的onTouchEvent()的方法在處理down事件的時候返回false,那麼才表明v1沒有處理事件。

6.若是經過setOnTouchListener()設置了一個有效的監聽到view中,那麼事件到達時會直接調用這個監聽方法而不會調用onTouchEvent(),並返回true,表示已經處理了該事件。

到這裏,onTouchEvent()返回值的含義應該很明確了,那麼super.onTouchEvent()返回值是什麼呢?看代碼也比較簡單,若是一個view不是clickable的或者不是longClickable的,那麼super.onTouchEvent()直接返回false,不然就進行onClick和onLongClick處理並返回true。可調用 setClickable(true),setLongClickable(true)來改變view的狀態,調用 setOnClickListener()和setOnLongClickListener()也是同樣的效果。

由上面結論,若是兩視圖是父弟關係,它們又互有重疊部分,點擊該重疊部分,先處理該事件的是下標比較大的那個視圖,若是這個視圖不想處理事件,才讓另一個處理。

四.onInterceptTouchEvent()

ViewGroup能夠調用它的onInterceptTouchEvent()方法去攔截子視圖的事件,這個方法默認返回的是false表示不攔截,若是在onInterceptTouchEvent()接收到down事件時返回了true,那麼接下來的down,move和up事件都會被 ViewGroup本身的onTouchEvent()方法所接收,全部的子視圖都接收不到事件,而ViewGroup本身的 onInterceptTouchEvent()方法也只有down事件會被傳遞過去,由於都由父View來處理,因此該方法再接收到move和up事件就沒有意義了,因此只會有down事件會被傳遞。註釋中的說法:There are no touch targets and this action is not an initial down,so this view group continues to intercept touches.

若是一個子視圖決定處理所有三個事件,那麼每次事件到來時都會先調用父view的onInterceptTouchEvent()方法,若是在某個事件上返回了true,那麼就會攔截到該事件以及隨後的事件到ViewGroup本身的onTouchEvent()方法處理,子視圖會接收到 ACTION_CANCEL事件。

舉例:若是子視圖處理了down事件,可是ViewGroup在move到來時攔截住了move事件,那麼子視圖就收不到接下來的move和up事件,會收到ACTION_CANCEL事件,而ViewGroup則會接收move和up事件,onInterceptTouchEvent()方法也只會接收到down和move事件。

攔截方法有時很是有用,例如ScrollView它會先把down事件交給子視圖處理,若是是點擊事件,就交給子視圖,若是判斷出來正在拖動子視圖,那麼會攔截住move事件,交由本身處理,調用overScrollBy()產生滾動。

固然若是本身重寫了ViewGroup的dispatchTouchEvent()方法就本身掌控了事件的分發過程,和上面的流程就不必定同樣了。

總結完了,相信開頭的全部問題都有了答案,有點繞人,理清了就明白了。

轉載自:http://www.bdqn.cn/news/201312/12158.shtml

相關文章
相關標籤/搜索