在自定義 View 中,常常須要處理 Android 事件分發的問題,尤爲在有多個輸入設備(如遙控、鼠標、遊戲手柄等)時,事件處理問題尤其突出。Android 事件分發機制,一直以來都是一個讓衆多開發者困擾的難點,至少筆者在工做的前幾年中,沒有特地研究它以前,就常常雲裏霧裏。
View 的事件分發機制實際上就是一個很是經典的責任鏈模式。
Android 責任鏈設計模式學習應用實例android
在講 Android 事件分發機制前,先簡單瞭解一些 MotionEvent,由於它就是這個「事件」。如下截取了部分源碼中的描述:設計模式
...... * <p> * Motion events describe movements in terms of an action code and a set of axis values. * The action code specifies the state change that occurred such as a pointer going * down or up. The axis values describe the position and other movement properties. * </p> ...... public final class MotionEvent extends InputEvent implements Parcelable { public static final int ACTION_DOWN = 0; public static final int ACTION_UP = 1; public static final int ACTION_MOVE = 2; ...... }
MotionEvent,顧名思義,動做事件的意思。它經過一個 action 碼和一套座標值來描述動做。action 碼指定了當如指針按下或者擡起等事件發生時的狀態改變,座標值則描述了事件在屏幕中的位置和其它動做屬性值。
平時觸摸屏幕時,一個最簡單的事件包括了「ACTION_DOWN」和「ACTION_UP」,「ACTION_DOWN」表示手指按下,而「「ACTION_UP」表示手指擡起來,這兩個 action 才構成了一個完整的事件。若是手指在屏幕上有移動,還會包含「ACTION_MOVE」,此時一個完整的事件就包括「ACTION_DOWN」,多個「ACTION_MOVE」,「ACTION_UP」。固然,實際工做中會有不少複雜的狀況出現,可能會出現一些其它的 aciton,本文爲了演示的方便,只考慮「ACTION_DOWN」和「ACTION_UP」的場景。ide
爲了演示事件分發機制的工做流程,這裏編寫一個示例來進行演示。整個 Acitivity 模擬 Boss 角色;在其界面中的最外層模擬 PM,繼承自 RelativeLayout,是一個父佈局;PM 下嵌套一層,也是一個父佈局,繼承自 RelativeLayout,用於模擬 Team Leader;最裏面一層是一個葉子 View,繼承自 Button,模擬 Programmer。效果圖及對應代碼分別以下。函數
以下的代碼中,須要重寫的方法 dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)均返回默認值,即 super.xxx。平時我們使用系統原生控件時,沒法修改它們的源碼,因此係統給的默認場景就是這樣的。
(1)Boss:EventDemoActivity佈局
public class EventDemoActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_event_demo); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("songzheweiwang","[EventDemoActivity-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i("songzheweiwang","[EventDemoActivity-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); return super.onTouchEvent(event); } }
(2)PM:ViewGroupOuter學習
public class ViewGroupOuter extends RelativeLayout { public ViewGroupOuter(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("songzheweiwang","[ViewGroupOuter-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("songzheweiwang","[ViewGroupOuter-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i("songzheweiwang","[ViewGroupOuter-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); return super.onTouchEvent(event); } }
(3)Team Leader:ViewGroupMiddlespa
public class ViewGroupMiddle extends RelativeLayout { public ViewGroupMiddle(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("songzheweiwang","[ViewGroupMiddle-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("songzheweiwang","[ViewGroupMiddle-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction())); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i("songzheweiwang","[ViewGroupMiddle-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); return super.onTouchEvent(event); } }
(4)Programmer:ViewInner
這裏先以 Button 爲例,由於 Button 默認是能夠處理 Touch 事件的,也就是說,事件傳到這裏時,能被完美地處理掉。設計
@SuppressLint("AppCompatCustomView") public class ViewInner extends Button{ public ViewInner(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.i("songzheweiwang","[ViewInner-->dispatchTouchEvent]event="+EventUtil.parseAction(event.getAction())); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i("songzheweiwang","[ViewInner-->onTouchEvent]event="+EventUtil.parseAction(event.getAction())); return super.onTouchEvent(event); } }
(5)輔助類:3d
public class EventUtil { public static String parseAction(int action) { String actionName = "Unknow:action=" + action; switch (action) { case MotionEvent.ACTION_DOWN: actionName = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: actionName = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: actionName = "ACTION_UP"; break; default: break; } return actionName; } }
點擊上圖中不一樣的區域,會有不一樣的結果。這裏點擊最中間的 View,點擊其餘區域的結果及分析,咱們在後面再介紹。指針
06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 06-07 13:35:23.524 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_UP 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_UP 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_UP
該事件包含了兩 action:ACTION_DOWN 和 ACTION_UP。前咱們說過,Programmer 完美處理好了事件,本次流程就到這裏爲止了,再也不傳遞,Boss 認爲團隊有能力處理這類任務,因此相似的任務也會一樣會交給手下的團隊,因此 ACTION_UP 也走了相似的流程,那麼整個事件就算完成了,由 Programmer 完美完成。整個事件的序列圖以下所示:
前面一直提到 Touch 事件的 3 個主要方法:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)和 onTouchEvent(MotionEvent ev),那麼這三個方法的功能到底是什麼呢?這裏能夠看看下面的表格的總結:
其實咱們能夠從函數名稱來大體判斷其功能,dispatchTouchEvent,分發觸摸事件,就是把事件傳遞下去,準確來講就是是否要傳遞到子 View 以及本身的 onInterceptTouchEvent 方法和 onTouchEvent 方法,也就是說,不只管子 View,還管自身剩下的兩個回調方法。onInterceptTouchEvent,事件攔截,它只管自身子 View,而不會影響到自身後面兩個方法的執行,若是攔截了,能夠記憶爲讓本身的手下們無事可作。這兩個方法容易混淆,須要重點理解和記憶。
在上述表格中還能夠看到,Activity 是沒法回調 onIntercepTouchEvent 方法的,由於這個方法是 ViewGroup 中的方法,而 Activity 也不是 View 體系中,不是視圖類,因此沒有這個方法。咱們能夠這樣記憶,Activity 是 Boss,不是打工行列中的一員,本身的任務就是讓下面的打工者沒去作事情,全部該方法對他來講,沒有意義。葉子 View 也沒有這個方法,由於本身沒有子 View 了,也沒有攔截的意義。
因爲這三個方法都是 boolean 值,再加上默認情形下會返回 super.xxx,這樣,每個方法都會有三種可選值。我們這裏先了解一下每一種取值會產生怎麼樣的結果。
Touch 事件發生時,Activity 的 dispatchTouchEvent 方法會將事件傳遞給最外層控件的 dispatchTouchEvent 方法,並由該控件進行分發下去。從根元素依次往下傳遞,一直到最裏面的葉子 View,或者中途被某個控件終止,才結束這個派發過程。其分發邏輯以下:
在當前控件的 dispatchTouchEvent 方法返回默認的方式時,其攔截邏輯以下:
上一節中介紹了 Touch 的 3 個主要方法的返回值下,對事件分發的處理邏輯。本節中,我們經過修改前面這三個方法中的返回值,來驗證事件的分發流程(注意:如下狀況下均點擊中間的 ViewInner 控件)。
1 06-07 19:15:53.220 25298-25298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 2 06-07 19:15:53.221 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 3 06-07 19:15:53.221 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 4 06-07 19:15:53.222 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN5 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 6 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP 7 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP 8 06-07 19:15:53.238 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP
參照第四節中的結論,ViewGroupMiddle 的 dispatchTouchEvent 返回 true,事件從 Acitivty,通過 ViewGroupOuter 分發到 ViewGroupMiddle 中,在其 dispatchTouchEvent 方法中處理。ViewGroupMiddle 的 onInterceptTouchEvent 和 onTouchEvent 均不會被調用,且事件也不會再往 ViewInner 中傳遞。既然事件是在 ViewGroupMiddle 的 dispatchTouchEvent 中被處理了,在 Boss EventDemoActivity 看來,本身手下的團隊有能力處理這類事件,因此 ACTION_UP 也被派發下來,走一樣的流程,直到全部事件處理完畢。
06-07 19:31:50.093 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
參照第四節中的結論,ViewGroupMiddle 的 dispatchTouchEvent 返回 true,事件從 Acitivty,通過 ViewGroupOuter 分發到 ViewGroupMiddle 中,且在 dispatchTouchEvent 方法中不處理此事件。ViewGroupMiddle 的 onInterceptTouchEvent 和 onTouchEvent 均不會被調用,且事件也不會再往 ViewInner 中傳遞。本身處理不了事件,傳遞給上一級的 onTouchEvent 來處理,上一級也沒能力處理,最後傳給了 EventDemoActivity 的 onTouchEvent。此時,在 Boss 看來,本身手下團隊處理不了這類事件,因此後面的事件就再也不傳遞下去,都有本身來處理。
06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 19:41:08.895 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 19:41:08.900 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 19:41:08.901 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
事件在 ViewGroupMiddle 中被攔截了,事件再也不派發到 ViewInner 中,而是交給本身的 onTouchEvent 來處理。前面說過,ViewGroupMiddle 繼承自 RelativeLayout,默認是沒有能力處理 Touch 事件的,因而就傳遞到上一級的 onTouchEvent 中,直到 EventDemoActivity 中的 onTouchEvent 方法。此時,在 Boss 看來,本身手下團隊處理不了這類事件,因此後面的事件就再也不傳遞下去,都有本身來處理。
06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
這種狀況下,和使用默認 super.onInterceptTouchEvent 時是同樣的,Log 中中的日誌也驗證了這一點。事件派發流程在第三節中詳細講解過,這裏就再也不贅述了。
06-07 19:53:51.516 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 06-07 19:53:51.582 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_UP
事件依次傳遞到 ViewInner 的 onTouchEvent 方法中,ViewInner 默認沒有能力處理該事件,傳遞到上一級 ViewGroupMiddle 中的 onTouchEvent 來處理。返回 true 表示被處理了,本次事件在此停止了。在 Boss 看來,手下團隊有能力處理這類事件,因此後面的 ACTION_UP 事件仍然往下分發了。這裏須要注意的是,ACTION_UP 在 ViewGroupMiddle 的 dispatchTouchEvent 執行後直接進入到其 onTouchEvent 方法中了,沒有通過 onInterceptTouchEvent 方法走,也沒有往 ViewInner 中分發。這個場景就好像,經過 ACTION_DOWN,ViewGroupMiddle 已經知道本身的手下 ViewInner 處理不了這類任務,因此當同類任務從上面領導發放到本身這裏的時候,就不用再繼續往下分發,而是直接直接就處理掉了。
06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
這裏在 activity_event_demo.xml 中使用 ViewGroupMiddle 時添加[android:clickable="true"],將 ViewGroupMiddle 設置爲默承認以處理 Touch 事件。當設置爲 false 值時,從日誌來看,代表 ViewGroupMiddle 不管是否有能力,都確實沒有處理事件,而是傳給了上級。
當 ViewGroupMiddle 中 onTouchEvent 返回默認的 super.onTouchEvent 時,咱們在第三節中分析過 ViewInner 有能處理和沒有能力處理兩種狀況下的事件處理邏輯,這裏筆者再也不贅述。如今還有一個結論須要讀者驗證,就是都在返回默認 super.xxx 狀況下,能夠在 ViewGroupMiddle 中 onTouchEvent 方法中打印出 super.onTouchEvent 的值。能夠發現,若是 ViewGroupMiddle 中 onTouchEvent 方法能夠處理事件,則值爲 true,若是沒有處理 Touch 事件的能力,則會返回 false。這一點在第四節中講過。
在前面分析打印 log 結果的時候,筆者都着重強調了要點擊正中心的 ViewInner。這是由於點擊不一樣的區域,會產生不一樣的邏輯處理結果。那麼點擊區域和事件分發結果有什麼樣的關係呢?下面將第三節中的例子,3 個主要方法都返回默認的 super.xxx 方法,由外到內依次點擊 Boss、PM、Team Leader、Programmer 四個區域。獲得了以下的 log 信息:
06-07 20:27:44.390 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:44.391 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP 06-07 20:27:48.298 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:48.338 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 20:27:48.339 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP 06-07 20:27:57.448 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:57.450 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN 06-07 20:27:57.514 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP 06-07 20:27:57.515 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
這四次觸摸事件的日誌結果用空格隔開,分析該 log 能夠發現:當點擊 Boss 區域時,裏面的三個控件均未觸發事件;當點擊 PM 區域時,Team Leader 和 Programmer 中的沒有任何動做;點擊 Team Leader 區域時,只有 Programmer 沒有觸發任何事件;當點擊 Programmer 區域時,4 個角色均被觸發。那麼這個結論就很顯而易見了:當點擊到 View 系統的某一層時,事件從外往內傳遞時,只到被點擊的那一層爲止,不會再派發到其子 View 中。這 4 個場景,是否是和咱們開篇第一節中提到的 4 種場景很類似呢?點擊到哪一個區域,說明本來安排的任務自己就應該由該職位的人來完成,其手下就徹底能夠當成是不存在的。
到目前爲止,Android 的事件分發和傳遞機制就分析完了。本文中 Touch 事件的 3 個主要方法返回值均有 3 種情形,因此會有多種邏輯處理組合。這裏選取了中間層 ViewGroupMiddle 來舉例,只是做爲表明來分析,筆者徹底能夠經過其它的組合來分析更多的可能狀況。若是分析中有不穩當或者不許確的地方,歡迎來拍磚
Android 自定義View篇(一)View繪製流程
Android 自定義View篇(二)Canvas詳解
Android 自定義View篇(三)Paint詳解
Android 自定義View篇(四)自定義屬性詳解