其實一直對Android的分發機制沒有深入的了解,就只是會用onClientListener()方法。不知道為什么這么用,所以今天就深入了解一下,Android的事件分發機制。
什么是Android的事件分發?
android事件分發機制 就是一個觸摸事件發生了,從一個窗口傳遞到一個視圖,再傳遞到另外一個視圖,最后被消費的過程。
事件分發的主角是?
Touch事件分發中只有兩個主角:ViewGroup和View。Activity的Touch事件事實上是調用它內部的ViewGroup的Touch事件,可以直接當成ViewGroup處理。
View在ViewGroup內,ViewGroup也可以在其他ViewGroup內,這時候把內部的ViewGroup當成View來分析。
ViewGroup的相關事件有三個:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相關事件只有兩個:dispatchTouchEvent、onTouchEvent。
事件分發的過程
(1) 事件從Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的View(ViewGroup)開始一直往下(子View)傳遞。子View可以通過onTouchEvent()對事件進行處理。
(2) 事件由父View(ViewGroup)傳遞給子View,ViewGroup可以通過onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。
(3) 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子View沒有消費事件,事件會反向往上傳遞,這時父View(ViewGroup)可以進行消費,如果還是沒有被消費的話,最后會到Activity的onTouchEvent()函數。
(4) 如果View沒有對ACTION_DOWN進行消費,之后的其他事件不會傳遞過來。
(5) OnTouchListener優先于onTouchEvent()對事件進行消費。
事件分發注意事項
事件分發在View中的處理:
1.onTouch是優先于onClick執行的,并且onTouch執行了兩次,一次是ACTION_DOWN,一次是ACTION_UP(你還可能會有多次ACTION_MOVE的執行,如果你手抖了一下)。因此事件傳遞的順序是先經過onTouch,再傳遞到onClick。
2.只要你觸摸到了任何一個控件,就一定會調用該控件的dispatchTouchEvent方法。那當我們去點擊按鈕的時候,就會去調用Button類里的dispatchTouchEvent方法,可是你會發現Button類里并沒有這個方法,那么就到它的父類TextView里去找一找,你會發現TextView里也沒有這個方法,那沒辦法了,只好繼續在TextView的父類View里找一找,這個時候你終于在View里找到了這個方法,示意圖如下:
3.首先在dispatchTouchEvent中最先執行的就是onTouch方法,因此onTouch肯定是要優先于onClick執行的。而如果在onTouch方法里返回了true,就會讓dispatchTouchEvent方法直接返回true,不會再繼續往下執行,onClick就不會再執行了。
參考:http://blog.csdn.net/guolin_blog/article/details/9097463
事件分發在ViewGroup中的處理:
1.只要你觸摸了任何控件,就一定會調用該控件的dispatchTouchEvent方法。這個說法沒錯,只不過還不完整而已。實際情況是,當你點擊了某個控件,首先會去調用該控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被點擊的相應控件,再去調用該控件的dispatchTouchEvent方法。如果我們點擊了MyLayout中的按鈕,會先去調用MyLayout的dispatchTouchEvent方法,可是你會發現MyLayout中并沒有這個方法。那就再到它的父類LinearLayout中找一找,發現也沒有這個方法。那只好繼續再找LinearLayout的父類ViewGroup,你終于在ViewGroup中看到了這個方法,按鈕的dispatchTouchEvent方法就是在這里調用的。修改后的示意圖如下所示:
參考:http://blog.csdn.net/guolin_blog/article/details/9153761
總結:
1. Android事件分發是先傳遞到ViewGroup,再由ViewGroup傳遞到View的。
2. 在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進行攔截,onInterceptTouchEvent方法返回true代表不允許事件繼續向子View傳遞,返回false代表不對事件進行攔截,默認返回false。
3. 子View中如果將傳遞的事件消費掉,ViewGroup中將無法接收到任何事件
什么是滑動沖突?
滑動沖突,就其本質來說,兩個不同方向(或者是同方向)的View,其中有一個是占主導地位的,每次總是搶著去處理外界的滑動行為,這樣就導致一種很別扭的用戶體驗,明明只是橫向的滑動了一下,縱向的列表卻在垂直方向發生了動作。就是說,這個占主導地位的View,每一次都身不由己的攔截了這個滑動的動作,因此,要解決滑動沖突,就是得明確告訴這個占主導地位的View,什么時候你該攔截,什么時候你不應該攔截,應該由下一層的View去處理這個滑動動作。
滑動沖突的解決方案:解決滑動沖突的關鍵,就是明確告知接收到Touch的View,是否需要攔截此次事件。
外部攔截法和內部攔截法
1.外部,故名思議是在父View的onInterceptTouchEvent處理.它針對3種不同的事件做處理,對于down, 返回false,除非你希望那個讓父View完全處理這3個事件。由于這里是false.所以同一事件序列的其他2個事件父view肯定能執行到(除非設置一個tag),對于move,看業務情況,返回true代表父類來消耗,false則表示子類,對于up,返回false.除非你想讓子View的click這種都無法用。
2.內部,重寫子元素的dispatchTouchEvent方法。默認情況下父View可以寫成除了down,其他都攔截,然后在子View里用parent,requestDisallowInterceptTouchEvent()