天天看點

Android中Touch 事件的分發和消費機制

Android 中與 Touch 事件相關的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能夠響應這些方法的控件包括:ViewGroup、View、Activity。方法與控件的對應關系如下表所示:

Touch 事件相關方法   方法功能    ViewGroup           View             Activity     
  public boolean dispatchTouchEvent(MotionEvent ev) 事件分發   Yes  Yes  Yes
  public boolean onInterceptTouchEvent(MotionEvent ev)  事件攔截   Yes  Yes  No
  public boolean onTouchEvent(MotionEvent ev) 事件響應   Yes  Yes  Yes

從這張表中我們可以看到 ViewGroup 和 View 對與 Touch 事件相關的三個方法均能響應,而 Activity 對 onInterceptTouchEvent(MotionEvent ev) 也就是事件攔截不進行響應。另外需要注意的是 View 對 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev) 的響應的前提是可以向該 View 中添加子 View,如果目前的 View 已經是一個最小的單元 View(比如 TextView),那麼就無法向這個最小 View 中添加子 View,也就無法向子 View 進行事件的分發和攔截,是以它沒有dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev),隻有 onTouchEvent(MotionEvent ev)。

一、Touch 事件分析

▐ 事件分發:public boolean dispatchTouchEvent(MotionEvent ev)

Touch 事件發生時 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法會以隧道方式(從根元素依次往下傳遞直到最内層子元素或在中間某一進制素中由于某一條件停止傳遞)将事件傳遞給最外層 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由該 View 的 dispatchTouchEvent(MotionEvent ev) 方法對事件進行分發。dispatchTouchEvent 的事件分發邏輯如下:

  • 如果 return true,事件會分發給目前 View 并由 dispatchTouchEvent 方法進行消費,同時事件會停止向下傳遞;
  • 如果 return false,事件分發分為兩種情況:
  1. 如果目前 View 擷取的事件直接來自 Activity,則會将事件傳回給 Activity 的 onTouchEvent 進行消費;
  2. 如果目前 View 擷取的事件來自外層父控件,則會将事件傳回給父 View 的  onTouchEvent 進行消費。
  • 如果傳回系統預設的 super.dispatchTouchEvent(ev),事件會自動的分發給目前 View 的 onInterceptTouchEvent 方法。

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

在外層 View 的 dispatchTouchEvent(MotionEvent ev) 方法傳回系統預設的 super.dispatchTouchEvent(ev) 情況下,事件會自動的分發給目前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件攔截邏輯如下:

  • 如果 onInterceptTouchEvent 傳回 true,則表示将事件進行攔截,并将攔截到的事件交由目前 View 的 onTouchEvent 進行處理;
  • 如果 onInterceptTouchEvent 傳回 false,則表示将事件放行,目前 View 上的事件會被傳遞到子 View 上,再由子 View 的 dispatchTouchEvent 來開始這個事件的分發;
  • 如果 onInterceptTouchEvent 傳回super.onInterceptTouchEvent(ev)和傳回false的處理邏輯一樣。

▐ 事件響應:public boolean onTouchEvent(MotionEvent ev)

在 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 傳回 true 或傳回 super.onInterceptTouchEvent(ev) 的情況下 onTouchEvent 會被調用。onTouchEvent 的事件響應邏輯如下:

  • 如果事件傳遞到目前 View 的 onTouchEvent 方法,而該方法傳回了 false,那麼這個事件會從目前 View 向上傳遞,并且都是由上層 View 的 onTouchEvent 來接收,如果傳遞到上面的 onTouchEvent 也傳回 false,這個事件就會“消失”,而且接收不到下一次事件。
  • 如果傳回了 true 則會接收并消費該事件。
  • 如果傳回 super.onTouchEvent(ev) 預設處理事件的邏輯和傳回 false 時相同。

到這裡,與 Touch 事件相關的三個方法就分析完畢了。下面的内容會通過各種不同的的測試案例來驗證上文中三個方法對事件的處理邏輯。

二、Touch 案例介紹

同樣在開始進行案例分析之前,我需要說明測試案例的結構,因為所有的測試都是針對這一個案例來進行的,測試中隻是通過修改每個控件中與 Touch 事件相關的三個方法的傳回值來展現不同的情況。先來看張圖:

Android中Touch 事件的分發和消費機制

上面的圖為測試案例的布局檔案 UI 顯示效果,布局檔案代碼如下:

Android中Touch 事件的分發和消費機制
<?xml version="1.0" encoding="utf-8"?>
<cn.sunzn.tevent.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#468AD7"
    android:gravity="center"
    android:orientation="vertical" >

    <cn.sunzn.tevent.TouchEventChilds
        android:id="@+id/childs"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:background="#E1110D"
        android:text="@string/hello" />

</cn.sunzn.tevent.TouchEventFather>      
Android中Touch 事件的分發和消費機制

藍色背景為一個自定義控件 TouchEventFather,該控件為外層 View,繼承自 LinearLayout,實作代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class TouchEventFather extends LinearLayout {

    public TouchEventFather(Context context) {
        super(context);
    }

    public TouchEventFather(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("sunzn", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.dispatchTouchEvent(ev);
    }

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("sunzn", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onInterceptTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent ev) {
        Log.d("sunzn", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onTouchEvent(ev);
    }

}      
Android中Touch 事件的分發和消費機制

紅色背景為一個自定義控件 TouchEventChilds,該控件為内層 View,為 TouchEventFather 的子 View,同樣繼承自 LinearLayout,實作代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class TouchEventChilds extends LinearLayout {

    public TouchEventChilds(Context context) {
        super(context);
    }

    public TouchEventChilds(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("sunzn", "TouchEventChilds | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.dispatchTouchEvent(ev);
    }

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("sunzn", "TouchEventChilds | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onInterceptTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent ev) {
        Log.d("sunzn", "TouchEventChilds | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onTouchEvent(ev);
    }

}      
Android中Touch 事件的分發和消費機制

接着實作 Activity 的代碼,因為控件所有的事件都是通過 Activity 的 dispatchTouchEvent 進行分發的;除此之外還需要重寫 Activity 的 onTouchEvent 方法,這是因為如果一個控件直接從 Activity 擷取到事件,這個事件會首先被傳遞到控件的 dispatchTouchEvent 方法,如果這個方法 return false,事件會以冒泡方式傳回給 Activity 的 onTouchEvent 進行消費。實作代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;

public class TouchEventActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.w("sunzn", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.dispatchTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent event) {
        Log.w("sunzn", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
        return super.onTouchEvent(event);
    }

}      
Android中Touch 事件的分發和消費機制

最後再附上 TouchEventUtil 的代碼,TouchEventUtil 中并沒有做多少事情,隻是将以上 2 個自定義控件中各個方法的 MotionEvent 集中到一個工具類中并将其對應的動作以 String 形式傳回,這樣處理更便于實時觀察控件的事件。代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.view.MotionEvent;

public class TouchEventUtil {
    
    public static String getTouchAction(int actionId) {
        String actionName = "Unknow:id=" + actionId;
        switch (actionId) {
        case MotionEvent.ACTION_DOWN:
            actionName = "ACTION_DOWN";
            break;
        case MotionEvent.ACTION_MOVE:
            actionName = "ACTION_MOVE";
            break;
        case MotionEvent.ACTION_UP:
            actionName = "ACTION_UP";
            break;
        case MotionEvent.ACTION_CANCEL:
            actionName = "ACTION_CANCEL";
            break;
        case MotionEvent.ACTION_OUTSIDE:
            actionName = "ACTION_OUTSIDE";
            break;
        }
        return actionName;
    }
    
}      
Android中Touch 事件的分發和消費機制

三、Touch 案例分析

 Case 1 

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather false super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
 W  05-10 03:41:19.743  414 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
 W  05-10 03:41:19.743  414 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_DOWN
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 false,表示對擷取到的事件停止向下傳遞,同時也不對該事件進行消費。由于 TouchEventFather 擷取的事件直接來自 TouchEventActivity ,則會将事件傳回給 TouchEventActivity  的 onTouchEvent 進行消費,最後直接由 TouchEventActivity 來響應手指移動和擡起事件。

  Case 2

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather true super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
 W  05-10 03:41:19.743  414 cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
 W  05-10 03:41:19.743  414 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 true,表示分發事件到 TouchEventFather 控件并由該控件的 dispatchTouchEvent 進行消費;TouchEventActivity 不斷的分發事件到 TouchEventFather 控件的dispatchTouchEvent,而 TouchEventFather 控件的 dispatchTouchEvent 也不斷的将擷取到的事件進行消費。

 Case 3

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather super.dispatchTouchEvent(ev) true super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
W 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
E 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
I 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN
D 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventFather | onTouchEvent --> ACTION_DOWN
W 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_DOWN
W 05-10 05:34:46.343 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.343 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.423 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.423 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.433 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.433 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.442 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
W 05-10 05:34:46.442 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev),表示對事件進行分發并向下傳遞給 TouchEventFather 控件的 onInterceptTouchEvent 方法,該方法傳回 true 表示對所擷取到的事件進行攔截并将事件傳遞給 TouchEventFather 控件的 onTouchEvent 進行處理,TouchEventFather 控件的 onTouchEvent 傳回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接将事件傳回給上級控件,由于 TouchEventFather 擷取的事件直接來自 TouchEventActivity,是以 TouchEventFather 控件的 onTouchEvent 會将事件以冒泡方式直接傳回給 TouchEventActivity 的 onTouchEvent 進行消費,後續的事件則會跳過 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。

 Case 4

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather super.dispatchTouchEvent(ev) false super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
W 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
E 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
I 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN
E 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_DOWN
I 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventChilds | onInterceptTouchEvent --> ACTION_DOWN
D 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventChilds | onTouchEvent --> ACTION_DOWN
D 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventFather | onTouchEvent --> ACTION_DOWN
W 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_DOWN
W 05-10 06:31:47.652 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.652 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.732 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.732 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.812 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.812 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.892 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
W 05-10 06:31:47.892 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev),表示對事件進行分發并向下傳遞給 TouchEventFather 控件的 onInterceptTouchEvent 方法,該方法傳回 false 表示事件會被放行并傳遞到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,同樣 TouchEventChilds 的 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev),表示對事件進行分發并向下傳遞給 TouchEventChilds 控件的 onInterceptTouchEvent 方法,TouchEventChilds 的 onInterceptTouchEvent 方法傳回 super.onInterceptTouchEvent(ev) 預設會将事件傳遞給 TouchEventChilds 的 onTouchEvent 進行處理,TouchEventChilds 的 onTouchEvent 傳回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接将事件傳回給上級控件,由于 TouchEventChilds 擷取的事件直接來自 TouchEventFather,是以 TouchEventChilds 控件的 onTouchEvent 會将事件以冒泡方式直接傳回給 TouchEventFather 的 onTouchEvent 進行消費,而 TouchEventFather 的 onTouchEvent 也傳回了 super.onTouchEvent(ev),同樣 TouchEventFather 的 onTouchEvent 也會将事件傳回給上級控件,而 TouchEventFather 擷取的事件直接來自 TouchEventActivity,是以 TouchEventFather 控件的 onTouchEvent 會将事件以冒泡方式直接傳回給 TouchEventActivity 的 onTouchEvent 進行消費,後續的事件則會跳過 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。

 Case 5

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather super.dispatchTouchEvent(ev) false super.onTouchEvent(ev)
TouchEventChilds true super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
 W  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
 E  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
 I  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN
 E  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_DOWN
 W  05-10 08:11:19.652 574  cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 08:11:19.652 574  cn.sunzn.tevent  sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 I  05-10 08:11:19.652 574  cn.sunzn.tevent  sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_MOVE
E  05-10 08:11:19.652 574 cn.sunzn.tevent sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 08:11:19.872 574 cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
E 05-10 08:11:19.872 574 cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_UP
I 05-10 08:11:19.872 574 cn.sunzn.tevent sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_UP
 E  05-10 08:11:19.872 574 cn.sunzn.tevent  sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,該控件的 dispatchTouchEvent 傳回super.dispatchTouchEvent(ev),事件會分發到 TouchEventFather 的 onInterceptTouchEvent,onInterceptTouchEvent 傳回 false 表示放行當先事件;事件會被傳遞到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 傳回 true 表示事件被分發到 TouchEventChilds 控件并由該控件的 dispatchTouchEvent 方法消費。後續的事件也會不斷的重複上面的邏輯最終被 TouchEventChilds 的 dispatchTouchEvent 消費。

 四、源碼下載下傳

 下載下傳位址:TouchEvent.rar

 五、相關文檔

 文檔位址:PRE_andevcon_mastering-the-android-touch-system.pdf

Android 中與 Touch 事件相關的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能夠響應這些方法的控件包括:ViewGroup、View、Activity。方法與控件的對應關系如下表所示:

Touch 事件相關方法   方法功能    ViewGroup           View             Activity     
  public boolean dispatchTouchEvent(MotionEvent ev) 事件分發   Yes  Yes  Yes
  public boolean onInterceptTouchEvent(MotionEvent ev)  事件攔截   Yes  Yes  No
  public boolean onTouchEvent(MotionEvent ev) 事件響應   Yes  Yes  Yes

從這張表中我們可以看到 ViewGroup 和 View 對與 Touch 事件相關的三個方法均能響應,而 Activity 對 onInterceptTouchEvent(MotionEvent ev) 也就是事件攔截不進行響應。另外需要注意的是 View 對 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev) 的響應的前提是可以向該 View 中添加子 View,如果目前的 View 已經是一個最小的單元 View(比如 TextView),那麼就無法向這個最小 View 中添加子 View,也就無法向子 View 進行事件的分發和攔截,是以它沒有dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev),隻有 onTouchEvent(MotionEvent ev)。

一、Touch 事件分析

▐ 事件分發:public boolean dispatchTouchEvent(MotionEvent ev)

Touch 事件發生時 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法會以隧道方式(從根元素依次往下傳遞直到最内層子元素或在中間某一進制素中由于某一條件停止傳遞)将事件傳遞給最外層 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由該 View 的 dispatchTouchEvent(MotionEvent ev) 方法對事件進行分發。dispatchTouchEvent 的事件分發邏輯如下:

  • 如果 return true,事件會分發給目前 View 并由 dispatchTouchEvent 方法進行消費,同時事件會停止向下傳遞;
  • 如果 return false,事件分發分為兩種情況:
  1. 如果目前 View 擷取的事件直接來自 Activity,則會将事件傳回給 Activity 的 onTouchEvent 進行消費;
  2. 如果目前 View 擷取的事件來自外層父控件,則會将事件傳回給父 View 的  onTouchEvent 進行消費。
  • 如果傳回系統預設的 super.dispatchTouchEvent(ev),事件會自動的分發給目前 View 的 onInterceptTouchEvent 方法。

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

在外層 View 的 dispatchTouchEvent(MotionEvent ev) 方法傳回系統預設的 super.dispatchTouchEvent(ev) 情況下,事件會自動的分發給目前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件攔截邏輯如下:

  • 如果 onInterceptTouchEvent 傳回 true,則表示将事件進行攔截,并将攔截到的事件交由目前 View 的 onTouchEvent 進行處理;
  • 如果 onInterceptTouchEvent 傳回 false,則表示将事件放行,目前 View 上的事件會被傳遞到子 View 上,再由子 View 的 dispatchTouchEvent 來開始這個事件的分發;
  • 如果 onInterceptTouchEvent 傳回 super.onInterceptTouchEvent(ev),事件預設會被攔截,并将攔截到的事件交由目前 View 的 onTouchEvent 進行處理。

▐ 事件響應:public boolean onTouchEvent(MotionEvent ev)

在 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 傳回 true 或傳回 super.onInterceptTouchEvent(ev) 的情況下 onTouchEvent 會被調用。onTouchEvent 的事件響應邏輯如下:

  • 如果事件傳遞到目前 View 的 onTouchEvent 方法,而該方法傳回了 false,那麼這個事件會從目前 View 向上傳遞,并且都是由上層 View 的 onTouchEvent 來接收,如果傳遞到上面的 onTouchEvent 也傳回 false,這個事件就會“消失”,而且接收不到下一次事件。
  • 如果傳回了 true 則會接收并消費該事件。
  • 如果傳回 super.onTouchEvent(ev) 預設處理事件的邏輯和傳回 false 時相同。

到這裡,與 Touch 事件相關的三個方法就分析完畢了。下面的内容會通過各種不同的的測試案例來驗證上文中三個方法對事件的處理邏輯。

二、Touch 案例介紹

同樣在開始進行案例分析之前,我需要說明測試案例的結構,因為所有的測試都是針對這一個案例來進行的,測試中隻是通過修改每個控件中與 Touch 事件相關的三個方法的傳回值來展現不同的情況。先來看張圖:

Android中Touch 事件的分發和消費機制

上面的圖為測試案例的布局檔案 UI 顯示效果,布局檔案代碼如下:

Android中Touch 事件的分發和消費機制
<?xml version="1.0" encoding="utf-8"?>
<cn.sunzn.tevent.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#468AD7"
    android:gravity="center"
    android:orientation="vertical" >

    <cn.sunzn.tevent.TouchEventChilds
        android:id="@+id/childs"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:background="#E1110D"
        android:text="@string/hello" />

</cn.sunzn.tevent.TouchEventFather>      
Android中Touch 事件的分發和消費機制

藍色背景為一個自定義控件 TouchEventFather,該控件為外層 View,繼承自 LinearLayout,實作代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class TouchEventFather extends LinearLayout {

    public TouchEventFather(Context context) {
        super(context);
    }

    public TouchEventFather(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("sunzn", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.dispatchTouchEvent(ev);
    }

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("sunzn", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onInterceptTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent ev) {
        Log.d("sunzn", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onTouchEvent(ev);
    }

}      
Android中Touch 事件的分發和消費機制

紅色背景為一個自定義控件 TouchEventChilds,該控件為内層 View,為 TouchEventFather 的子 View,同樣繼承自 LinearLayout,實作代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class TouchEventChilds extends LinearLayout {

    public TouchEventChilds(Context context) {
        super(context);
    }

    public TouchEventChilds(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("sunzn", "TouchEventChilds | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.dispatchTouchEvent(ev);
    }

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("sunzn", "TouchEventChilds | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onInterceptTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent ev) {
        Log.d("sunzn", "TouchEventChilds | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.onTouchEvent(ev);
    }

}      
Android中Touch 事件的分發和消費機制

接着實作 Activity 的代碼,因為控件所有的事件都是通過 Activity 的 dispatchTouchEvent 進行分發的;除此之外還需要重寫 Activity 的 onTouchEvent 方法,這是因為如果一個控件直接從 Activity 擷取到事件,這個事件會首先被傳遞到控件的 dispatchTouchEvent 方法,如果這個方法 return false,事件會以冒泡方式傳回給 Activity 的 onTouchEvent 進行消費。實作代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;

public class TouchEventActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.w("sunzn", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
        return super.dispatchTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent event) {
        Log.w("sunzn", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
        return super.onTouchEvent(event);
    }

}      
Android中Touch 事件的分發和消費機制

最後再附上 TouchEventUtil 的代碼,TouchEventUtil 中并沒有做多少事情,隻是将以上 2 個自定義控件中各個方法的 MotionEvent 集中到一個工具類中并将其對應的動作以 String 形式傳回,這樣處理更便于實時觀察控件的事件。代碼如下:

Android中Touch 事件的分發和消費機制
package cn.sunzn.tevent;

import android.view.MotionEvent;

public class TouchEventUtil {
    
    public static String getTouchAction(int actionId) {
        String actionName = "Unknow:id=" + actionId;
        switch (actionId) {
        case MotionEvent.ACTION_DOWN:
            actionName = "ACTION_DOWN";
            break;
        case MotionEvent.ACTION_MOVE:
            actionName = "ACTION_MOVE";
            break;
        case MotionEvent.ACTION_UP:
            actionName = "ACTION_UP";
            break;
        case MotionEvent.ACTION_CANCEL:
            actionName = "ACTION_CANCEL";
            break;
        case MotionEvent.ACTION_OUTSIDE:
            actionName = "ACTION_OUTSIDE";
            break;
        }
        return actionName;
    }
    
}      
Android中Touch 事件的分發和消費機制

三、Touch 案例分析

 Case 1 

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather false super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
 W  05-10 03:41:19.743  414 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
 W  05-10 03:41:19.743  414 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_DOWN
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 false,表示對擷取到的事件停止向下傳遞,同時也不對該事件進行消費。由于 TouchEventFather 擷取的事件直接來自 TouchEventActivity ,則會将事件傳回給 TouchEventActivity  的 onTouchEvent 進行消費,最後直接由 TouchEventActivity 來響應手指移動和擡起事件。

  Case 2

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather true super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
 W  05-10 03:41:19.743  414 cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
 W  05-10 03:41:19.743  414 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
E  05-10 03:41:19.743  414  cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 true,表示分發事件到 TouchEventFather 控件并由該控件的 dispatchTouchEvent 進行消費;TouchEventActivity 不斷的分發事件到 TouchEventFather 控件的dispatchTouchEvent,而 TouchEventFather 控件的 dispatchTouchEvent 也不斷的将擷取到的事件進行消費。

 Case 3

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather super.dispatchTouchEvent(ev) true super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
W 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
E 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
I 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN
D 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventFather | onTouchEvent --> ACTION_DOWN
W 05-10 05:34:46.333 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_DOWN
W 05-10 05:34:46.343 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.343 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.423 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.423 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.433 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.433 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 05:34:46.442 481 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
W 05-10 05:34:46.442 481 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev),表示對事件進行分發并向下傳遞給 TouchEventFather 控件的 onInterceptTouchEvent 方法,該方法傳回 true 表示對所擷取到的事件進行攔截并将事件傳遞給 TouchEventFather 控件的 onTouchEvent 進行處理,TouchEventFather 控件的 onTouchEvent 傳回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接将事件傳回給上級控件,由于 TouchEventFather 擷取的事件直接來自 TouchEventActivity,是以 TouchEventFather 控件的 onTouchEvent 會将事件以冒泡方式直接傳回給 TouchEventActivity 的 onTouchEvent 進行消費,後續的事件則會跳過 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。

 Case 4

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather super.dispatchTouchEvent(ev) false super.onTouchEvent(ev)
TouchEventChilds super.dispatchTouchEvent(ev) super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
W 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
E 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
I 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN
E 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_DOWN
I 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventChilds | onInterceptTouchEvent --> ACTION_DOWN
D 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventChilds | onTouchEvent --> ACTION_DOWN
D 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventFather | onTouchEvent --> ACTION_DOWN
W 05-10 06:31:47.565 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_DOWN
W 05-10 06:31:47.652 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.652 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.732 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.732 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.812 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.812 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_MOVE
W 05-10 06:31:47.892 512 cn.sunzn.tevent sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
W 05-10 06:31:47.892 512 cn.sunzn.tevent sunzn   TouchEventActivity | onTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,而該控件的 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev),表示對事件進行分發并向下傳遞給 TouchEventFather 控件的 onInterceptTouchEvent 方法,該方法傳回 false 表示事件會被放行并傳遞到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,同樣 TouchEventChilds 的 dispatchTouchEvent 傳回 super.dispatchTouchEvent(ev),表示對事件進行分發并向下傳遞給 TouchEventChilds 控件的 onInterceptTouchEvent 方法,TouchEventChilds 的 onInterceptTouchEvent 方法傳回 super.onInterceptTouchEvent(ev) 預設會将事件傳遞給 TouchEventChilds 的 onTouchEvent 進行處理,TouchEventChilds 的 onTouchEvent 傳回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接将事件傳回給上級控件,由于 TouchEventChilds 擷取的事件直接來自 TouchEventFather,是以 TouchEventChilds 控件的 onTouchEvent 會将事件以冒泡方式直接傳回給 TouchEventFather 的 onTouchEvent 進行消費,而 TouchEventFather 的 onTouchEvent 也傳回了 super.onTouchEvent(ev),同樣 TouchEventFather 的 onTouchEvent 也會将事件傳回給上級控件,而 TouchEventFather 擷取的事件直接來自 TouchEventActivity,是以 TouchEventFather 控件的 onTouchEvent 會将事件以冒泡方式直接傳回給 TouchEventActivity 的 onTouchEvent 進行消費,後續的事件則會跳過 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。

 Case 5

攔截條件
控件名稱 dispatchTouchEvent 傳回值 onInterceptTouchEvent 傳回值 onTouchEvent 傳回值
TouchEventActivity super.dispatchTouchEvent(ev) --- super.onTouchEvent(ev)
TouchEventFather super.dispatchTouchEvent(ev) false super.onTouchEvent(ev)
TouchEventChilds true super.onInterceptTouchEvent(ev) super.onTouchEvent(ev)
運作結果
Level Time PID Application Tag  Text
 W  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN
 E  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_DOWN
 I  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN
 E  05-10 08:11:18.403 574  cn.sunzn.tevent  sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_DOWN
 W  05-10 08:11:19.652 574  cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE
 E  05-10 08:11:19.652 574  cn.sunzn.tevent  sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_MOVE
 I  05-10 08:11:19.652 574  cn.sunzn.tevent  sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_MOVE
E  05-10 08:11:19.652 574 cn.sunzn.tevent sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_MOVE
 W  05-10 08:11:19.872 574 cn.sunzn.tevent  sunzn   TouchEventActivity | dispatchTouchEvent --> ACTION_UP
E 05-10 08:11:19.872 574 cn.sunzn.tevent sunzn   TouchEventFather | dispatchTouchEvent --> ACTION_UP
I 05-10 08:11:19.872 574 cn.sunzn.tevent sunzn   TouchEventFather | onInterceptTouchEvent --> ACTION_UP
 E  05-10 08:11:19.872 574 cn.sunzn.tevent  sunzn   TouchEventChilds | dispatchTouchEvent --> ACTION_UP
結果分析
代碼運作後,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控件的 dispatchTouchEvent,該控件的 dispatchTouchEvent 傳回super.dispatchTouchEvent(ev),事件會分發到 TouchEventFather 的 onInterceptTouchEvent,onInterceptTouchEvent 傳回 false 表示放行當先事件;事件會被傳遞到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 傳回 true 表示事件被分發到 TouchEventChilds 控件并由該控件的 dispatchTouchEvent 方法消費。後續的事件也會不斷的重複上面的邏輯最終被 TouchEventChilds 的 dispatchTouchEvent 消費。

 四、源碼下載下傳

 下載下傳位址:TouchEvent.rar

 五、相關文檔

 文檔位址:PRE_andevcon_mastering-the-android-touch-system.pdf

繼續閱讀