Android事件分發解析

來源:互聯網
上載者:User

標籤:

事件分發機制
一.點擊事件傳播路徑
1.點擊事件TouchEvent最先是到達Activity的,然後傳給Activity對應的window,再傳給DecorView,再傳給id為content的ViewGroup,即我們通過setContentView設定的ViewGroup,以此到最後的view。我們編程所能控制的由Activity,ViewGroup和View。
 
2.要把點擊事件傳給一個View或ViewGroup,要調用其dispatchTouchEvent(),要判斷該View或ViewGroup是否攔截,要調用其onInterceptTouchEvent(),要判斷該View或ViewGroup是否消耗,要調用其onTouchEvent()。子view沒有onInterceptTouchEvent(),所以傳給在子view的點擊事件都是直接判斷是否要消耗。當一個事件被消耗了就會消失掉。
 
 
3.自訂的ViewGroup和View預設都是不攔截點擊事件的,ViewGroup決定攔截與否是通過onInterceptTouchEvent()的返回值決定的;ViewGroup決定消耗事件與否是通過onTouchEvent()決定的。View因為沒有onInterceptTouchEvent(),所有決定攔截與否跟消耗與否是通過onTouchEvent()的返回值決定的,true表示攔截,false表示不攔截。注意分清攔截和消耗。
ViewGroup的onInterceptTouchEvent()如下
 


4.在子View的dispatchTouchEvent()內部有這樣一段代碼
 
其邏輯是,如果View設定了onTouchListener,則其onTouchListener的onTouch()方法會被調用。如果該方法返回false,則會調用onTouchEvent(),但是如果該方法返回true,則onTouchEvent()方法不會被調用,且這種情況屬於自view消耗了點擊事件。而我們平時的setOnClickListener()設定的OnClickListener是在onTouchEvent()裡被執行的,並且如果我們沒有重寫onTouchEvent()的話,onTouchEvent()會返回true。
 




二.具體情況分析
1.當ViewGroup和View都不攔截不消耗點擊事件ACTION_DOWN時流程如下
 
並且之後的ACTION_MOVE和ACTION_UP都不會再傳給ViewGroup和VIew,而是直接交給Activity的onTouchEvent()處理
2,若ViewGroup不攔截,在view也不消耗,然當事件再次來到ViewGroup的onTouchEvent()時,如果ViewGroup決定要消耗掉該ACTION_DOWN,則以後該TouchEvent的ACTION_MOVE和ACTION_UP都會傳到該ViewGroup,並且不會調用其onInterceptTouchEvent()
 
3.當ViewGroup的onInterceptTouchEvent()攔截了ACTION_DOWN時,點擊事件不再傳給子view,這時候會調用ViewGroup的onTouchEvent()判斷是否要消耗ACTION_DOWN,如果不消耗,則會調用Activity的onTouchEvent(),並且以後該TouchEvent系列的ACTION_MOVE和ACTION_UP都不會再傳給ViewGroup即其以下的子View。
 
如果消耗,則以後該TouchEvent系列的ACTION_MOVE和ACTION_UP都會傳給該ViewGroup(當不會傳給其子view),並且不會再調用該ViewGroup的onInterceptTouchEvent()。
 


4.對於ACTION_DOWN,當ViewGroup不攔截,子view決定消耗。那麼該事件之後的ACTION_MOVE和ACTION_UP都會正常傳進來,即先經過ViewGroup的dispatchTouchEvent(),再到onInterceptTouchEvent(),接著到子view的onTouchEvent(),這時即使不被View和ViewGroup消耗,同一事件體系的ACTION_MOVE和ACTION_UP都會正常傳進來ViewGroup和View的。注意,這時當子view不消耗事件時,事件不會傳給ViewGroup的onTouchEvent(),而是直接交給Activity
 
5.當ACTION_DOWN到ViewGroup時,如果ViewGroup不攔截,事件傳給子view,如果子view消耗了,但當第一個ACTION_MOVE到ViewGroup時,ViewGroup攔截,這時,這個ACTION_MOVE會傳給子View,觸發子view的onTouchEvent,不過這時ACTION_MOVE變為ACTION_CANCEL,如果子view消耗了,則該ACTION_CANCEL消失,如果子View不消耗,則該ACTION_CANCEL傳給ACtivity的onTouchEvent()。然而,不管子View消耗不消耗ACTION_CANCEL,第二個開始的ACTION_MOVE和ACTION_UP都不會再傳到子view了,二是得到ViewGroup的dispatchTouchEvent(),再直接傳給ViewGroup的onTouchEvent()ViewGroup決定消耗則消耗,不消耗則回傳給Activity。
 
6.當ViewGroup不攔截ACTION_DOWN和ACTION_MOVE,但子view攔截了。然後ViewGroup攔截ACTION_UP,這時,ACTION_UP會以ACTION_CANCEL形式傳給子view,子View處理ACTION_CANCEL則事件消失,不處理則傳回給Activity。
 
三.Android內建View分析
自訂的View預設是不消耗點擊事件的。而Android內建的View則不一定,要該View的clickable和longClickable同時為false才不消耗點擊事件,主要有一個不為false,則要消耗點擊事件。TextView預設是不消耗點擊事件的,除非為它設定了onTouchListener或onClickListener。Button則是消耗點擊事件的,除非把他設定為不可點擊。(嚴格來說,事件被判斷為onClick必須是ACTION_DOWN,ACTION_MOVE,ATION_UP都發生在View的大小範圍內)。


四.活動處突處理方法
1.內部攔截法:
思路:ViewGroup先不攔截點擊事件,先交給子view,子view先處理,當應由ViewGroup攔截的條件成立時,把事件交給ViewGroup處理。
實現:ViewGroup在onInterceptTouchEvent()中不攔截ACTION_DOWN,交給子view。子view的dispatchTouchEvent()中遇到ACTION_DOWN時調用ViewGroup的setDisallowInterceptTouchEvent(true),即設定該TouchEvent系列的ACTION_MOVE和ACTION_UP不經過ViewGroup。然後這時候子view在onTouchEvent()一定要消耗ACTION_DOWN,否則之後的事件連ViewGroup都不會傳到。好了,現在之後的ACTION_DOWN會經過ViewGroup的dispatchTouchEvent()然後直接傳到子view的dispatchTouchEvent(),而不會再交給ViewGroup的onInterceptTouchEvent(),這時候子view可以根據情況消耗ACTION_MOVE,當子view覺得得把之後的點擊事件交給父容器時,即調用父容器的setDisallowInterceptTouchEvent(false)。而在重寫ViewGroup的時候對ACTION_DOWN之外的事件都設定為攔截,使用之後的點擊事件在經過ViewGroup的dispatchTouchEvent()後就會到達其onInterceptTouchEvent(),攔截之後就給ViewGroup的onTouchEvent()處理。
邏輯圖如下:
 
代碼實現:
ViewGroup
 
子View
 
 


2.外部攔截
實現:ViewGroup對ACTION_DOWN不攔截,而子View要消耗ACTION_DOWN(否則這一系列的點擊事件就不會再傳到該ViewGroup和View)。接著對於ACTION_MOVE,如果ViewGroup不攔截,則會傳給子View,如果ViewGroup想攔截,則之後的事件都會傳給ViewGroup的onTouchEvent()處理。並且,ViewGroup不用去攔截ACTION_UP。
 
代碼:


 
 


3.滑動處突處理方法三:
首先要明白onInterceptTouchEvent()的返回值一般也就是dispatchTouchEvent()的返回值。所以,從dispatchTouchEvent()的返回值就可以直接做出攔截與否。而且dispatchTouchEvent()在每個事件流程都會被調用到,這一點與onInterceptTouchEvent()不同,onInterceptTouchEvent()當ViewGroup決定攔截並且在onTouchEvent裡消耗了事件後,同一系列的事件是不會再經過它的。
按照這個情況,就可以做出一種更加靈活的處突處理方法,但是這種方法比上面兩種麻煩多了。
思路:在ViewGroup中自己做攔截處理和調用子View的dispatchTouchEvent()傳遞事件。
在dispatchTouchEvent()中分別判斷ACTION_DOWN,ACTION_MOVE,ACTION_UP。
當ACTION_DOWN到來時,要返回true,表示接下來的事件都要傳過來。然後調用子View的dispatchTouchEvent(),把ACTION_DOWN事件給子View,子View才能有正常的邏輯。
當ACTION_MOVE到來時,如果ViewGroup要攔截,則不要調用子View的dispatchTouchEvent(),這樣事件就只停留在ViewGroup而不會傳到子View。如果在某個ACTION_DOWN,ViewGroup又不想攔截,而想交給子View處理,則直接調用子view的dispatchTouchEvent()。當ACTION_UP到來時,如果子View需要正常執行時,就調用其dispatchTouchEvent()。
注意不要調用super.dispatchTouchEvent(),因為這裡的攔截邏輯是我們自己做的,所以甚至可以不重寫ViewGroup的onInterceptTouchEvent()和onTouchEvent()。


Android事件分發解析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.