#### **七、事件攔截機制分析**
**1、什么是觸摸事件**
觸摸事件顧名思義就是捕獲觸摸后產生的事件。當點擊一個按鈕時,通常就會產生兩個或三個事件:
* 按鈕按下——事件一
* 如果不小心滑動了一點——事件二
* 當手抬起——事件三
Android為觸摸事件封裝了一個類——[MotionEvent](https://www.androidos.net.cn/android/5.0.1_r1/xref/frameworks/base/core/java/android/view/MotionEvent.java),這個類很重要,只要是重寫觸摸相關的方法,參數一般都含有MotionEvent。
首先我們先看一下這個類的源碼樹結構,如圖







觸摸事件其實就是一個工作類型加坐標而已,但是Android的view結構是樹形結構,可能層層嵌套,一層層地堆疊起來的,那觸摸事件到底改分給誰呢?同一事件子view和父ViewGroup都有可能想要進行處理,這就產生了事件攔截。
**2、實例分析事件攔截機制**
一個總經理——MyViewGroupA,最外層的ViewGroup
一個部長——MyViewGroupB,中間的ViewGroup
一個底層人民,干活的你——MyView,在最底層。
布局文件[activity_main.xml](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/ViewLayout/app/src/main/res/layout/activity_main.xml)如下所示
~~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.imooc.viewlayout.MyViewGroupA
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_bright">
<com.imooc.viewlayout.MyViewGroupB
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/holo_green_dark">
<com.imooc.viewlayout.MyView
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@android:color/darker_gray" />
</com.imooc.viewlayout.MyViewGroupB>
</com.imooc.viewlayout.MyViewGroupA>
</RelativeLayout>
~~~
[MainActivity.java](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/ViewLayout/app/src/main/java/com/imooc/viewlayout/MainActivity.java)
~~~
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
~~~
通過點擊三個布局中的View,打印log來研究事件攔截機制。
**①、分析**
對于ViewGroup來說,重寫以下三個方法
~~~
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("xys", "ViewGroupA dispatchTouchEvent event.getAction()= " + ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("xys", "ViewGroupA onInterceptTouchEvent event.getAction()= " + ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("xys", "ViewGroupA onTouchEvent event.getAction()= " + event.getAction());
return super.onTouchEvent(event);
}
~~~
對于View來說,重寫以下兩個方法
~~~
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("xys", "View onTouchEvent event.getAction()= " + event.getAction());
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d("xys", "View dispatchTouchEvent event.getAction()= " + event.getAction());
return super.dispatchTouchEvent(event);
}
~~~
從上面的代碼就得知,ViewGroup級別較高,比View多一個方法onInterceptTouchEvent(),從字面就能猜到這個方法是事件攔截的核心方法。
首先,我們點擊最里面的View(MyView)打印log如下所示
~~~
02-06 00:20:03.450 2763-2763/com.imooc.viewlayout D/xys: ViewGroupA dispatchTouchEvent event.getAction()= 0
02-06 00:20:03.451 2763-2763/com.imooc.viewlayout D/xys: ViewGroupA onInterceptTouchEvent event.getAction()= 0
02-06 00:20:03.451 2763-2763/com.imooc.viewlayout D/xys: ViewGroupB dispatchTouchEvent event.getAction()= 0
02-06 00:20:03.451 2763-2763/com.imooc.viewlayout D/xys: ViewGroupB onInterceptTouchEvent event.getAction()= 0
02-06 00:20:03.451 2763-2763/com.imooc.viewlayout D/xys: View dispatchTouchEvent event.getAction()= 0
02-06 00:20:03.451 2763-2763/com.imooc.viewlayout D/xys: View onTouchEvent event.getAction()= 0
02-06 00:20:03.453 2763-2763/com.imooc.viewlayout D/xys: ViewGroupB onTouchEvent event.getAction()= 0
02-06 00:20:03.455 2763-2763/com.imooc.viewlayout D/xys: ViewGroupA onTouchEvent event.getAction()= 0
~~~
可以得出正常情況下,
* 時間的傳遞順序是:
**總經理(MyViewGroupA)→部長(MyViewGroupB)→你(MyView)。**
**事件傳遞時先執行dispatchTouchEvent方法,再執行onInterceptTouchEvent方法。**
* 事件處理的順序:
**你(MyView)→部長(MyViewGroupB)→總經理(MyViewGroupA)。**
**事件處理都是執行onTouchEvent 方法**
* 事件傳遞的返回值
**True,攔截,不繼續;False,不攔截,繼續流程**
* 事件處理的返回值
**True,處理了,不用審核了;False,給上級處理。**
初始情況下,返回值都是False。
一般情況下,在事件傳遞過程中,只關心onInterceptTouchEvent方法,而dispatchTouchEvent方法盡管是事件分發的第一步,但是一般不改寫這個方法,我們將上面的流程再形象一點如下圖所示