【朝花夕拾】Android自定義View篇之(五)Android事件分發機制(上)Touch三個重要方法的處理邏輯

前言html

       轉載請註明,轉自【http://www.javashuo.com/article/p-oqmfqqwl-e.html】謝謝!android

       在自定義View中,常常須要處理Android事件分發的問題,尤爲在有多個輸入設備(如遙控、鼠標、遊戲手柄等)時,事件處理問題尤其突出。Android事件分發機制,一直以來都是一個讓衆多開發者困擾的難點,至少筆者在工做的前幾年中,沒有特地研究它以前,就常常雲裏霧裏。實際上,該問題的「七寸」就是dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)這三個方法和MotionEvent事件實體,我們這裏索性稱它們爲「四大惡人」吧。本文將主要經過示例演示的方式來打這個「七寸」吧。程序員

        本文的主要內容以下:編程

 

 

 

1、事件分發機制與生活場景ide

       Android的事件分發機制和生活中的不少場景有着類似之處,可能Android的不少設計靈感就是來源於生活吧。函數

  一、示例中的四個角色佈局

       在衆多示例當中,有一個常常被拿來舉例的經典場景就是PM(項目經理)、Team Leader、Programmer之間工做安排的問題,我們這裏也用這個場景來類比,另外再加一個Boss的角色,以便於理解。如今這個公司中四個角色職位從高到低依次爲,Boss > PM > Team Leader > Programmer。測試

 

  二、可能出現的場景spa

       通常來講,一個尋常的工做流程是: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能夠總體做爲一個角色來看,只是爲了後面看代碼和日誌方便對應,才分開爲兩個角色的。

 

2、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」的場景。

 

3、示例代碼

       爲了演示事件分發機制的工做流程,這裏編寫一個示例來進行演示。整個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

 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 }

 

  三、日誌

      點擊上圖中不一樣的區域,會有不一樣的結果。這裏點擊最中間的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

 

  四、結果分析

       該事件包含了兩action:ACTION_DOWN和ACTION_UP。前咱們說過,Programmer完美處理好了事件,本次流程就到這裏爲止了,再也不傳遞,Boss認爲團隊有能力處理這類任務,因此相似的任務也會一樣會交給手下的團隊,因此ACTION_UP也走了相似的流程,那麼整個事件就算完成了,由Programmer完美完成。整個事件的序列圖以下所示:

 

  五、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)小點的狀況①的場景是一致的,這裏我們再也不分析日誌畫流程圖了。

 

4、Touch事件主要方法說明

  一、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,這樣,每個方法都會有三種可選值。我們這裏先了解一下每一種取值會產生怎麼樣的結果。

 

  二、事件分發: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。

 

  三、事件攔截:public boolean onInterceptTouchEvent(MotionEvent ev) 

       在當前控件的dispatchTouchEvent方法返回默認的方式時,其攔截邏輯以下:

        1)return true,表示將事件進行攔截,並將攔截到的事件交給當前控件的onTouchEvent來處理。

        2)return false,表示將事件放行,事件會被傳遞到子View上,並由子View的dispatchTouchEvent方法繼續派發。

        3)return super.onInterceptTouchEvent(ev),和返回false的邏輯同樣。

 

  四、事件響應: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。

 

5、Touch的3個主要方法返回值對事件分發影響的案例分析

       上一節中介紹了Touch的3個主要方法的返回值下,對事件分發的處理邏輯。本節中,我們經過修改前面這三個方法中的返回值,來驗證事件的分發流程(注意:如下狀況下均點擊中間的ViewInner控件)。

  一、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也被派發下來,走一樣的流程,直到全部事件處理完畢。

 

  二、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看來,本身手下團隊處理不了這類事件,因此後面的事件就再也不傳遞下去,都有本身來處理。

 

  三、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看來,本身手下團隊處理不了這類事件,因此後面的事件就再也不傳遞下去,都有本身來處理。

 

四、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中中的日誌也驗證了這一點。事件派發流程在第三節中詳細講解過,這裏就再也不贅述了。

 

五、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處理不了這類任務,因此當同類任務從上面領導發放到本身這裏的時候,就不用再繼續往下分發,而是直接直接就處理掉了。

 

  六、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。這一點在第四節中講過。

 

6、當觸摸其它區域時分析

        在前面分析打印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來舉例,只是做爲表明來分析,筆者徹底能夠經過其它的組合來分析更多的可能狀況。若是分析中有不穩當或者不許確的地方,歡迎來拍磚。

相關文章
相關標籤/搜索