簡簡單單講清楚android事件分發。css
事件分發是:當發生了一個事件時,在屏幕上找到一個合適的控件來處理這個事件的過程。html
由於一個界面上控件如此之多,發生一個事件後總要尋找一個合適來處理事件吧。這個過程就叫作事件分發的機制。java
那麼屏幕上都會發生什麼事件呢?來看下常常要處理的4種事件(這些事件在android中會被封裝成 MotionEvent 對象):android
事件 | 簡介 |
---|---|
ACTION_DOWN | 手指 初次接觸到屏幕 時觸發。 |
ACTION_MOVE | 手指 在屏幕上滑動 時觸發,會會屢次觸發。 |
ACTION_UP | 手指 離開屏幕 時觸發。 |
ACTION_CANCEL | 事件 被上層攔截 時觸發。 |
爲了在屏幕中如何尋找合適的處理事件的控件,咱們先來看下屏幕中都有什麼控件。git
假設咱們寫了以下的一個佈局:segmentfault
<com.xyl.touchevent.test.RootView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="300dp" android:background="#4E5268" android:layout_margin="20dp" tools:context="com.xyl.touchevent.MainActivity"> <com.xyl.touchevent.test.ViewGroupA android:background="#95C3FA" android:layout_width="200dp" android:layout_height="200dp"> <com.xyl.touchevent.test.View1 android:background="#BDDA66" android:layout_width="130dp" android:layout_height="130dp"/> </com.xyl.touchevent.test.ViewGroupA> <com.xyl.touchevent.test.View2 android:layout_alignParentRight="true" android:background="#BDDA66" android:layout_width="80dp" android:layout_height="80dp"/> </com.xyl.touchevent.test.RootView>
View結構:oop
上面的代碼運行到界面上由圖中這幾部分組成:佈局
能夠看到在上面的View結構中莫名多出來的兩個東西,PhoneWindow 和 DecorView ,這兩個咱們並無在Layout文件中定義過,可是爲何會存在呢?spa
1. PhoneWindow
PhoneWindow是 Window 的惟一實現類,是全部視圖的最頂層容器,視圖的外觀和行爲都歸他管,不管是背景顯示,標題欄仍是事件處理都是他管理的範疇。
2. DecorView
DecorView是 PhoneWindow 的一個內部類,就是跟在 PhoneWindow 身邊專業爲 PhoneWindow 服務的,除了本身要幹活以外,也負責消息的傳遞,PhoneWindow 的指示經過 DecorView 傳遞給下面的 View,而下面 View 的信息也經過 DecorView 回傳給 PhoneWindow。
在如上圖View的樹形結構中,事件發生時,最早由Activity接收,而後再一層層的向下層傳遞,直到找到合適的處理控件。大體以下:.net
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View |
---|
可是若是事件傳遞到最後的View仍是沒有找到合適的View消費事件,那麼事件就會向相反的方向傳遞,最終傳遞給Activity,若是最後 Activity 也沒有處理,本次事件纔會被拋棄:
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View |
---|
當事件發生時,ViewGroup會在dispatchTouchEvent方法中先看本身可否處理事件,若是不能再去遍歷子View查找合適的處理控件。若是到最後result仍是false,表示全部的子View都不能處理,纔會調用自身的onTouchEvent來處理。
根據上面提到的原理,ViewGroup事件傳遞的僞代碼以下:
public boolean dispatchTouchEvent(MotionEvent ev) { boolean result = false; // 默認狀態爲沒有消費過 if (!onInterceptTouchEvent(ev)) { // 若是沒有攔截交給子View result = child.dispatchTouchEvent(ev); } if (!result) { // 若是事件沒有被消費,詢問自身onTouchEvent result = onTouchEvent(ev); } return result; }
爲了方便理解,這裏貼出一張網上的別人的一張圖:
上面的流程中存在部分不合理內容,請你們選擇性接受。
事件返回時 dispatchTouchEvent 直接指向了父View的 onTouchEvent 這一部分是不合理的,實際上它僅僅是給了父View的 dispatchTouchEvent 一個 false 返回值,父View根據返回值來調用自身的 onTouchEvent。
ViewGroup 是根據 onInterceptTouchEvent 的返回值來肯定是調用子View的 dispatchTouchEvent 仍是自身的 onTouchEvent, 並無將調用交給 onInterceptTouchEvent。
若是在遍歷過程當中,發現了有能夠處理事件的控件,就中止事件的傳遞,事件被消耗。
本文參考資料:
http://www.gcssloop.com/customview/dispatch-touchevent-theory
https://juejin.im/entry/58df5b33570c35005798493c
https://juejin.im/entry/580042082e958a0055b6cbbc
http://www.javashuo.com/article/p-nwtgajhz-no.html
https://www.jianshu.com/p/31e20def82c2
http://www.javashuo.com/article/p-hnslpwns-mp.html
https://zhuanlan.zhihu.com/p/27608989
http://gityuan.com/2015/09/19/android-touch/
http://wuxiaolong.me/2015/12/19/MotionEvent/
推薦閱讀:
推薦閱讀: