1. 程式人生 > >Android的View 事件傳遞

Android的View 事件傳遞

兩張 -s 對象 isp 上傳 popu 歷史記錄 layout ron

歡迎轉載,請附出處:
http://blog.csdn.net/as02446418/article/details/47422891

1、基礎知識

(1) 全部 Touch 事件都被封裝成了 MotionEvent 對象,包含 Touch 的位置、時間、歷史記錄以及第幾個手指(多指觸摸)等。

(2) 事件類型分為 ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL,每一個事件都是以 ACTION_DOWN 開始 ACTION_UP 結束。

(3) 對事件的處理包含三類,分別為傳遞——dispatchTouchEvent()函數、攔截——onInterceptTouchEvent()函數、消費——onTouchEvent()函數和 OnTouchListener

2、傳遞流程

(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()對事件進行消費。
上面的消費即表示對應函數返回值為 true。

很多其它請直接閱讀 PDF 英文原文:Mastering the Android Touch System

演示樣例代碼:Demo@Github

附上兩張原文中流程圖:
(1) View 不處理事件流程圖

技術分享圖片
(2) View 處理事件流程圖
技術分享圖片

3、最後說幾句

Android Touch事件
假設布局層次為
Layout0
Layout1
Layout2
Layout3

假設誰都沒有去interceptTouch,同一時候誰都沒有處理onTouch事件。


那麽Layout0->intercept Layout1->intercept Layout2->intercept Layout3->intercept
Layout3->onTouch Layout2->onTouch Layout1->onTouch Layout0->onTouch
因為誰都沒有消費ACTION_DOWN事件,興許的MOVE,UP事件將不會傳進來。

假設Layout2 intercept了,可是不消費onTouch
那麽Layout0->intercept Layout1->intercept Layout2->intercept
Layout2->onTouch Layout1->onTouch Layout0->onTouch
興許事件不會傳入

假設Layout2 intercept了,同一時候消費了。
那麽 0->intercept 1->intercept 2->intercept 2->onTouch
0->intercept 1->intercept 2->onTouch
0->intercept 1->intercept 2->onTouch
0->intercept 1->intercept 2->onTouch

假設Layout2 intercept了。不消費,Layout1消費了。
那麽0->intercept 1->intercept 2->intercept
2->onTouch 1->onTouch
0->intercept 1->onTouch
0->intercept 1->onTouch
0->intercept 1->onTouch

總結一下。

規律就是
假設當前Layout intercept了,那麽子View和子ViewGroup都沒有機會去獲得Touch事件了。

假設當前Layout並不消費事件的話,這個事件會一直向上冒泡,直到某個父Layout的onTouchEvent消費了這個事件。

假設沒有不論什麽一個父Layout消費這個事件,那麽興許的事件都不會被接受。
假設在冒泡過程中有某個Layout消費了這個事件。那麽這個Layout的全部父Layout的intercept仍然會被調用。可是當前Layout的intercept不會再被調用了。

直接調用onTouch事件。

另外,對於底層的View來說,有一種方法能夠阻止父層的View截獲touch事件,就是調用getParent().requestDisallowInterceptTouchEvent(true);方法。一旦底層View收到touch的action後調用這種方法那麽父層View就不會再調用onInterceptTouchEvent了,也無法截獲以後的action。在實踐過程中發現ListView在滾動的時候會調用這種方法。

使得action不能被攔截。

Android的View 事件傳遞