在自定義View中,經常需要處理Android事件分發的問題,尤其在有多個輸入裝置(如遙控、滑鼠、遊戲搖桿等)時,事件處理問題尤為突出。Android事件分發機制,一直以來都是一個讓衆多開發者困擾的難點,至少筆者在工作的前幾年中,沒有特意研究它之前,就經常雲裡霧裡。實際上,該問題的“七寸”就是dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)這三個方法和MotionEvent事件實體,咱們這裡索性稱它們為“四大惡人”吧。本文将主要通過示例示範的方式來打這個“七寸”吧。
前言
轉載請注明,轉自【https://www.cnblogs.com/andy-songwei/p/10998855.html】謝謝!
在自定義View中,經常需要處理Android事件分發的問題,尤其在有多個輸入裝置(如遙控、滑鼠、遊戲搖桿等)時,事件處理問題尤為突出。Android事件分發機制,一直以來都是一個讓衆多開發者困擾的難點,至少筆者在工作的前幾年中,沒有特意研究它之前,就經常雲裡霧裡。實際上,該問題的“七寸”就是dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)這三個方法和MotionEvent事件實體,咱們這裡索性稱它們為“四大惡人”吧。本文将主要通過示例示範的方式來打這個“七寸”吧。
本文的主要内容如下:
一、事件分發機制與生活場景
Android的事件分發機制和生活中的很多場景有着相似之處,可能Android的很多設計靈感就是來源于生活吧。
1、示例中的四個角色
在衆多示例當中,有一個經常被拿來舉例的經典場景就是PM(項目經理)、Team Leader、Programmer之間工作安排的問題,咱們這裡也用這個場景來類比,另外再加一個Boss的角色,以便于了解。現在這個公司中四個角色職位從高到低依次為,Boss > PM > Team Leader > Programmer。
2、可能出現的場景
一般來說,一個尋常的工作流程是:Boss想到有個功能很流行,就會訓示PM去辦,PM會分發給Team Leader,而Team Leader會安排Proggrammer去程式設計實作。Boss是決策者,事件來源于他;PM和Team Leader是管理者,負責一層層把任務派發給自己的下屬;Programmer是具體來做開發的,是以事件最後落在他的身上了,最後由他來完成。但是現實工作中,工作并不總是這個流程,還有很多其它場景,比如:
(1)市面上出現了一很火的行業,做智能手機。本公司是否需要也涉足這個行業,需要Boss自己開董事會來做決策。那麼這個事情就是Boss應該處理的事情,他就不會派發給PM,Boss以下的員工看來,就跟沒有任何事情一樣。
(2)Boss确定了智能手機是一個很有前景的行業,确定了要做,于是就召集PM,做好立項工作。那這個立項工作就是這個PM的工作了,如何立項,需要招聘什麼樣人等各項準備工作,PM就得自己做好整個計劃,事情就到他這裡為止了,不會再往下派發。
(3)做手機掙了錢,Boss決定獎勵一些表現優異的Programmer。這需要先安排PM,PM然後安排下面的Team Leader對下面的Programmer們做綜合考量,将優秀者報上去。這就是Team Leader需要完成的工作,他也無法再傳下去。
(4)如前面說到的,Boss要求做一個市面上很流行的功能,經過PM和Team Leader層層派發到了Programmer手上。雖然事情派發到了Programmer手上,但也有兩種情形:
1)在Programmer能力範圍内,做得很完美。這種情況事情就在這裡被處理掉了,無需再傳遞出去了。後續Boss一系列的類似功能也會繼續派發下來,也以這樣的流程來走下去。
2)Programmer能力有限,做不了。這種情況下,他就需要告訴Team Leader這一情況,把這個任務再依次傳遞給自己的上司。這樣又有兩種情況:①Team Leader 或者PM本身也是研發出身,開發能力也很強,就把這個開發任務給完成了,這樣事情也就到此為止不再傳遞了。在Boss看來,下面的團隊有能力開發好這類需求,于是後續Boss一系列的類似功能也會繼續派發下來。由于上一次的任務中,Team Leader或者PM知道自己的手下處理不了這類任務,是以當上一級把任務分發到自己這一層的時候,就不會再繼續往下分發,而是自己親自處完成。②Team Leader和PM都是管理出身,這個功能他們也不會做。于是就層層往上報,最後到老闆那裡,老闆自己處理,是不了了之還是再找招人,或者自己也是個大牛程式員自己可以開發出來,這個決策由老闆來做。老闆知道自己下面的團隊完成不了這一系列任務,後續一系列這類功能,就不再派發下去了。
......
上述列出了一些比較有代表性的可能情況,下面咱們根據這些情況,來了解事件的分發機制。其實這裡PM和Team Leader可以整體作為一個角色來看,隻是為了後面看代碼和日志友善對應,才分開為兩個角色的。
二、MotionEvent簡介
在講Android事件分發機制前,先簡單了解一些MotionEvent,因為它就是這個“事件”。以下截取了部分源碼中的描述:
1 ......
2 * <p>
3 * Motion events describe movements in terms of an action code and a set of axis values.
4 * The action code specifies the state change that occurred such as a pointer going
5 * down or up. The axis values describe the position and other movement properties.
6 * </p>
7 ......
8 public final class MotionEvent extends InputEvent implements Parcelable {
9 public static final int ACTION_DOWN = 0;
10 public static final int ACTION_UP = 1;
11 public static final int ACTION_MOVE = 2;
12 ......
13 }
MotionEvent,顧名思義,動作事件的意思。它通過一個action碼和一套坐标值來描述動作。action碼指定了當如指針按下或者擡起等事件發生時的狀态改變,坐标值則描述了事件在螢幕中的位置和其它動作屬性值。如下内容為MotionEvent的toString方法列印出來的結果:
MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=173.0, y[0]=138.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x2, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=34268429, downTime=34268429, deviceId=8, source=0x1002 }
從這裡可以看到,事件發生的action,時間,坐标等很多資訊。本文中暫時隻關注aciton這個字段,“action=ACTION_DOWN”表示了按下事件。
平時觸摸螢幕時,一個最簡單的事件包括了“ACTION_DOWN”和“ACTION_UP”,“ACTION_DOWN”表示手指按下,而““ACTION_UP”表示手指擡起來,這兩個action才構成了一個完整的事件。如果手指在螢幕上有移動,還會包含“ACTION_MOVE”,此時一個完整的事件就包括“ACTION_DOWN”,多個“ACTION_MOVE”,“ACTION_UP”。當然,實際工作中會有很多複雜的情況出現,可能會出現一些其它的aciton,本文為了示範的友善,隻考慮“ACTION_DOWN”和“ACTION_UP”的場景。
三、示例代碼
為了示範事件分發機制的工作流程,這裡編寫一個示例來進行示範。整個Acitivity模拟Boss角色;在其界面中的最外層模拟PM,繼承自RelativeLayout,是一個父布局;PM下嵌套一層,也是一個父布局,繼承自RelativeLayout,用于模拟Team Leader;最裡面一層是一個葉子View,繼承自Button,模拟Programmer。效果圖及對應代碼分别如下。
1、示範界面
2、預設場景下的代碼示例
如下的代碼中,需要重寫的方法dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)均傳回預設值,即super.xxx。平時咱們使用系統原生控件時,無法修改它們的源碼,是以系統給的預設場景就是這樣的。
(1)Boss:EventDemoActivity
1 public class EventDemoActivity extends AppCompatActivity {
2
3 @Override
4 protected void onCreate(Bundle savedInstanceState) {
5 super.onCreate(savedInstanceState);
6 setContentView(R.layout.activity_event_demo);
7 }
8
9 @Override
10 public boolean dispatchTouchEvent(MotionEvent ev) {
11 Log.i("songzheweiwang","[EventDemoActivity-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
12 return super.dispatchTouchEvent(ev);
13 }
14
15 @Override
16 public boolean onTouchEvent(MotionEvent event) {
17 Log.i("songzheweiwang","[EventDemoActivity-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
18 return super.onTouchEvent(event);
19 }
20 }
該Activity的布局檔案為
1 //==========================activity_event_demo.xml===========================
2 <?xml version="1.0" encoding="utf-8"?>
3 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent">
6
7 <com.example.demos.customviewdemo.ViewGroupOuter
8 android:layout_width="300dp"
9 android:layout_height="300dp"
10 android:layout_centerInParent="true"
11 android:background="@android:color/holo_orange_dark">
12
13 <com.example.demos.customviewdemo.ViewGroupMiddle
14 android:layout_width="200dp"
15 android:layout_height="200dp"
16 android:layout_centerInParent="true"
17 android:background="@android:color/holo_blue_dark">
18
19 <com.example.demos.customviewdemo.ViewInner
20 android:id="@+id/viewInner"
21 android:layout_width="100dp"
22 android:layout_height="100dp"
23 android:layout_centerInParent="true"
24 android:background="@android:color/holo_green_dark"/>
25 </com.example.demos.customviewdemo.ViewGroupMiddle>
26 </com.example.demos.customviewdemo.ViewGroupOuter>
27 </RelativeLayout>
(2)PM:ViewGroupOuter
1 public class ViewGroupOuter extends RelativeLayout {
2
3 public ViewGroupOuter(Context context, @Nullable AttributeSet attrs) {
4 super(context, attrs);
5 }
6
7 @Override
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9 Log.i("songzheweiwang","[ViewGroupOuter-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
10 return super.dispatchTouchEvent(ev);
11 }
12
13 @Override
14 public boolean onInterceptTouchEvent(MotionEvent ev) {
15 Log.i("songzheweiwang","[ViewGroupOuter-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
16 return super.onInterceptTouchEvent(ev);
17 }
18
19 @Override
20 public boolean onTouchEvent(MotionEvent event) {
21 Log.i("songzheweiwang","[ViewGroupOuter-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
22 return super.onTouchEvent(event);
23 }
24 }
(3)Team Leader:ViewGroupMiddle
1 public class ViewGroupMiddle extends RelativeLayout {
2
3 public ViewGroupMiddle(Context context, @Nullable AttributeSet attrs) {
4 super(context, attrs);
5 }
6
7 @Override
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9 Log.i("songzheweiwang","[ViewGroupMiddle-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
10 return super.dispatchTouchEvent(ev);
11 }
12
13 @Override
14 public boolean onInterceptTouchEvent(MotionEvent ev) {
15 Log.i("songzheweiwang","[ViewGroupMiddle-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
16 return super.onInterceptTouchEvent(ev);
17 }
18
19 @Override
20 public boolean onTouchEvent(MotionEvent event) {
21 Log.i("songzheweiwang","[ViewGroupMiddle-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
22 return super.onTouchEvent(event);
23 }
24 }
(4)Programmer:ViewInner
這裡先以Button為例,因為Button預設是可以處理Touch事件的,也就是說,事件傳到這裡時,能被完美地處理掉。
1 @SuppressLint("AppCompatCustomView")
2 public class ViewInner extends Button{
3
4 public ViewInner(Context context, AttributeSet attrs) {
5 super(context, attrs);
6 }
7
8 @Override
9 public boolean dispatchTouchEvent(MotionEvent event) {
10 Log.i("songzheweiwang","[ViewInner-->dispatchTouchEvent]event="+EventUtil.parseAction(event.getAction()));
11 return super.dispatchTouchEvent(event);
12 }
13
14 @Override
15 public boolean onTouchEvent(MotionEvent event) {
16 Log.i("songzheweiwang","[ViewInner-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
17 return super.onTouchEvent(event);
18 }
19 }
(5)輔助類:
1 public class EventUtil {
2 public static String parseAction(int action) {
3 String actionName = "Unknow:action=" + action;
4 switch (action) {
5 case MotionEvent.ACTION_DOWN:
6 actionName = "ACTION_DOWN";
7 break;
8 case MotionEvent.ACTION_MOVE:
9 actionName = "ACTION_MOVE";
10 break;
11 case MotionEvent.ACTION_UP:
12 actionName = "ACTION_UP";
13 break;
14 default:
15 break;
16 }
17 return actionName;
18 }
19 }
3、日志
點選上圖中不同的區域,會有不同的結果。這裡點選最中間的View,點選其他區域的結果及分析,我們在後面再介紹。
1 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
6 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
7 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
8 06-07 13:35:23.524 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
9 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP
10 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP
11 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_UP
13 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_UP
14 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_UP
4、結果分析
該事件包含了兩action:ACTION_DOWN和ACTION_UP。前我們說過,Programmer完美處理好了事件,本次流程就到這裡為止了,不再傳遞,Boss認為團隊有能力處理這類任務,是以類似的任務也會同樣會交給手下的團隊,是以ACTION_UP也走了類似的流程,那麼整個事件就算完成了,由Programmer完美完成。整個事件的序列圖如下所示:
5、ViewInner沒有能力處理的情況
上面的例子中,ViewInner是一個Button,它預設是有能力處理這次Touch事件的。但是如果這是一個預設沒有能力處理該時間的控件,又會是一種怎樣的情形呢?咱們把ViewInner改為繼承View再看看結果(仍然點選中間的ViewInner)。
1 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
6 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
7 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
8 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
9 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 15:04:25.865 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 15:04:25.865 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
此時,整個事件的流程就變成了現在這樣,直覺地表現為如下的流程圖:
為什麼是這種結果呢?因為ViewInner繼承自View,其預設情況下,是沒有處理Touch事件的能力的。是以Programmer處理不了了,就一層一層往上報告,由于ViewGroupMiddle和ViewGroupOuter都繼承自RelativeLayout,預設也是沒有處理Touch事件的能力的,是以最後ACTION_DOWN事件就回到了Boss這裡,由Boss自己來處理。Boss發現自己手下的團隊無法處理這類事件,是以後面的ACTION_UP事件就自己處理了,而沒有再往下派發了。這一點,和第二節中的第(4)點的第2)小點的情況②的場景是一緻的。
如果在activity_event_demo.xml中為各個控件(包括父布局)加上屬性[android:clickable="true"],或者在Activity中為對應控件添加監聽點選事件,那麼這個控件就有了處理Touch事件的能力了,就和之前使用Button的場景一樣的。讀者還可以試試在ViewInnner為View時,其父布局有處理Touch事件的能力時的場景(注意,要點選ViewInner來測試),那麼這就是和第二節中的第(4)點的第2)小點的情況①的場景是一緻的,這裡咱們不再分析日志畫流程圖了。
四、Touch事件主要方法說明
1、Touch的三個主要方法概述
前面一直提到Touch事件的3個主要方法:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent ev),那麼這三個方法的功能究竟是什麼呢?這裡可以看看下面的表格的總結:
其實我們可以從函數名稱來大緻判斷其功能,dispatchTouchEvent,分發觸摸事件,就是把事件傳遞下去,準确來說就是是否要傳遞到子View以及自己的onInterceptTouchEvent方法和onTouchEvent方法,也就是說,不僅管子Viiew,還管自身剩下的兩個回調方法。onInterceptTouchEvent,事件攔截,它隻管自身子View,而不會影響到自身後面兩個方法的執行,如果攔截了,可以記憶為讓自己的手下們無事可做。這兩個方法容易混淆,需要重點了解和記憶。
在上述表格中還可以看到,Activity是無法回調onIntercepTouchEvent方法的,因為這個方法是ViewGroup中的方法,而Activity也不是View體系中,不是視圖類,是以沒有這個方法。我們可以這樣記憶,Activity是Boss,不是打工行列中的一員,自己的任務就是讓下面的打工者們去做事情,所有該方法對他來說,沒有意義。葉子View也沒有這個方法,因為自己沒有子View了,也沒有攔截的意義。
由于這三個方法都是boolean值,再加上預設情形下會傳回super.xxx,這樣,每一個方法都會有三種可選值。咱們這裡先了解一下每一種取值會産生怎麼樣的結果。
2、事件分發:public boolean dispatchTouchEvent(MotionEvent ev)
Touch事件發生時,Activity的dispatchTouchEvent方法會将事件傳遞給最外層控件的dispatchTouchEvent方法,并由該控件進行分發下去。從根元素依次往下傳遞,一直到最裡面的葉子View,或者中途被某個控件終止,才結束這個派發過程。其分發邏輯如下:
(1)如果 return true,事件會分發到目前控件的dispatchTouchEvent方法中處理。同時事件停止往下分發,且目前控件的onInterceptTouchEvent和onTouchEvent都不會執行。
(2)如果 return false,事件停止往下派發,且目前控件的onInterceptTouchEvent和onTouchEvent也都不會執行。同時将事件傳回給上一級的onTouchEvent事件,由上一級去決定處理還是繼續往上傳遞,自己不處理。
(3)傳回預設的super.dispatchTouchEvent,事件會自動分發給目前View的onInterceptTouchEvent。
3、事件攔截:public boolean onInterceptTouchEvent(MotionEvent ev)
在目前控件的dispatchTouchEvent方法傳回預設的方式時,其攔截邏輯如下:
1)return true,表示将事件進行攔截,并将攔截到的事件交給目前控件的onTouchEvent來處理。
2)return false,表示将事件放行,事件會被傳遞到子View上,并由子View的dispatchTouchEvent方法繼續派發。
3)return super.onInterceptTouchEvent(ev),和傳回false的邏輯一樣。
4、事件響應:public boolean onTouchEvent(MotionEvent ev)
(1)該方法會被執行的情形有如下兩種:
1)子View沒有處理事件,将事件傳回來;
2)目前控件中dispatchTouchEvent傳回預設的super.dispatchTouchEvent的情況下,且該控件的onInterceptTouchEvent傳回false或者預設的super.onInterceptTouchEvent時。
(2)onTouchEvent方法響應邏輯如下:
1)傳回true,目前事件會被處理掉。
2)傳回false,目前事件不會被處理,傳回給上一級的onTouchEvent方法來處理。
3)傳回super.onTouchEvent,如果自己有能力處理該事件,則會處理,此時super.onTouchEvent的值為true;否則,如果自己沒有能力處理該事件,則将事件傳回到上一級中的onTouchEvent方法中處理,目前super.onTouchEvent的值為false。
五、Touch的3個主要方法傳回值對事件分發影響的案例分析
上一節中介紹了Touch的3個主要方法的傳回值下,對事件分發的處理邏輯。本節中,咱們通過修改前面這三個方法中的傳回值,來驗證事件的分發流程(注意:以下情況下均點選中間的ViewInner控件)。
1、ViewGroupMiddle中dispatchTouchEvent傳回true,其它均傳回預設值時
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_DOWN
5 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也被派發下來,走同樣的流程,直到所有事件處理完畢。
2、ViewGroupMiddle中dispatchTouchEvent傳回false,其它均傳回預設值時
1 06-07 19:31:50.093 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
6 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
7 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
8 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看來,自己手下團隊處理不了這類事件,是以後面的事件就不再傳遞下去,都有自己來處理。
3、ViewGroupMiddle中onInterceptTouchEvent傳回true,其它均傳回預設值時
1 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
6 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
7 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
8 06-07 19:41:08.895 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
9 06-07 19:41:08.900 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
10 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看來,自己手下團隊處理不了這類事件,是以後面的事件就不再傳遞下去,都有自己來處理。
4、ViewGroupMiddle中onInterceptTouchEvent傳回false,其它均傳回預設值時
1 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
6 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
7 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
8 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
9 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
這種情況下,和使用預設super.onInterceptTouchEvent時是一樣的,Log中中的日志也驗證了這一點。事件派發流程在第三節中詳細講解過,這裡就不再贅述了。
5、ViewGroupMiddle中onTouchEvent為true,其它均傳回預設值時
1 06-07 19:53:51.516 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
6 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
7 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
8 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
9 06-07 19:53:51.582 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
10 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP
11 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP
12 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP
13 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處理不了這類任務,是以當同類任務從上面上司發放到自己這裡的時候,就不用再繼續往下分發,而是直接直接就處理掉了。
6、ViewGroupMiddle中onTouchEvent為false,其它均傳回預設值時
1 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
6 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
7 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
8 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
9 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 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無論是否有能力,都确實沒有處理事件,而是傳給了上級。
7, 均為預設值時
當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資訊:
1 06-07 20:27:44.390 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 20:27:44.391 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
3 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
4 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
5
6 06-07 20:27:48.298 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
7 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
8 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
9 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 20:27:48.338 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 20:27:48.339 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
13
14 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
15 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
16 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
17 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
18 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
19 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
20 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
21 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
22 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
23 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
24
25 06-07 20:27:57.448 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
26 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
27 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
28 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
29 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
30 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
31 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
32 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
33 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
34 06-07 20:27:57.450 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
35 06-07 20:27:57.514 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
36 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來舉例,隻是作為代表來分析,筆者完全可以通過其它的組合來分析更多的可能情況。如果分析中有不妥當或者不準确的地方,歡迎來拍磚。