本文將介紹 Activity翩伪、ViewGroup凡辱、View 三者觸摸事件傳遞機制。一次完整的觸摸事件傳遞包括三個階段:分發(fā)秒紧、攔截和消費绢陌。接下來就從實際運行效果上來對觸摸事件傳遞機制有一個基本的認(rèn)識。
1 事件類型
觸摸事件對應(yīng)的是 android.view.MotionEvent
類熔恢,觸摸事件的類型非常多脐湾,我們主要關(guān)注以下三種:
-
ACTION_DOWN
:用戶手指的按下操作,代表著一次觸摸事件的開始叙淌。 -
ACTION_MOVE
:用戶手指的移動操作秤掌。 -
ACTION_UP
:用戶手指的抬起操作,代表一次觸摸事件的結(jié)束鹰霍。
一次完整的觸摸事件包括一次 ACTION_DOWN
闻鉴、0 到多次 ACTION_MOVE
和一次 ACTION_UP
。
2 觸摸事件傳遞的三個階段
觸摸事件傳遞的三個階段茂洒,分別對應(yīng)著三個方法孟岛。
- 分發(fā):
public boolean dispatchTouchEvent(MotionEvent ev)
,這個方法在 Activity、ViewGroup渠羞、View 中均有定義斤贰。 - 攔截:
public boolean onInterceptTouchEvent(MotionEvent ev)
,這個方法只在 ViewGroup 中有定義次询。 - 消費:
public boolean onTouchEvent(MotionEvent event)
腋舌,這個方法在 Activity、ViewGroup渗蟹、View 中均有定義块饺。
可以看到,這三個方法都返回一個 boolean 值雌芽,那么接下來就讓我們一起來看一下這三個方法在返回不同值時的運行效果授艰。
3 運行效果
首先需要創(chuàng)建一個項目。這里我已經(jīng)創(chuàng)建好了世落,可以直接下載運行:https://github.com/gaoshijie365/ExampleTouchEvent淮腾。
- 為
MainActivity
覆寫分發(fā)和消費的方法并加入日志代碼:
private static final String TAG = "Activity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent: " + MotionEvent.actionToString(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: " + MotionEvent.actionToString(event.getAction()));
return super.onTouchEvent(event);
}
- 新建
MyViewGroup
類并覆寫觸摸事件傳遞三個方法并加入日志代碼:
public class MyViewGroup extends RelativeLayout {
private static final String TAG = "ViewGroup";
// 此處省略構(gòu)造器的定義
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent: " + MotionEvent.actionToString(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "onInterceptTouchEvent: " + MotionEvent.actionToString(ev.getAction()));
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: " + MotionEvent.actionToString(event.getAction()));
return super.onTouchEvent(event);
}
}
- 新建
MyView
類并覆寫觸摸事件傳遞兩個方法并加入日志代碼,這里繼承自 ImageView屉佳,你也可以繼承自任何 View:
public class MyView extends ImageView {
private static final String TAG = "View";
// 此處省略構(gòu)造器定義
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent: " + MotionEvent.actionToString(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: " + MotionEvent.actionToString(event.getAction()));
return super.onTouchEvent(event);
}
}
- 布局代碼
activity_main.xml
如下:
<?xml version="1.0" encoding="utf-8"?>
<com.gaoshijie.android.myapplication.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewGroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<com.gaoshijie.android.myapplication.MyView
android:id="@+id/view"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="@color/colorAccent"
android:src="@mipmap/ic_launcher" />
</com.gaoshijie.android.myapplication.MyViewGroup>
接下來分別修改 Activity谷朝、ViewGroup、View 的事件處理相關(guān)方法的返回值并觀察日志武花。這里舉一種情況的例子圆凰,剩下的我就不一一列舉了,大家可以自行下載源碼修改返回值測試一下体箕。
在所有覆寫的方法都返回默認(rèn)值专钉,即調(diào)用 super 的時候,日志如下:
Activity: dispatchTouchEvent: ACTION_DOWN
ViewGroup: dispatchTouchEvent: ACTION_DOWN
ViewGroup: onInterceptTouchEvent: ACTION_DOWN
View: dispatchTouchEvent: ACTION_DOWN
View: onTouchEvent: ACTION_DOWN
ViewGroup: onTouchEvent: ACTION_DOWN
Activity: onTouchEvent: ACTION_DOWN
從這個日志中可以看出默認(rèn)情況下累铅,事件由 Activity 分發(fā)到 ViewGroup跃须,再由 ViewGroup 分發(fā)到 View,最后又反方向調(diào)用了 onTouchEvent 方法去處理娃兽。
4 總結(jié)
當(dāng)我們運行完所有的情況之后菇民,可以發(fā)現(xiàn),對于事件傳遞三個階段的方法投储,不同的返回值對應(yīng)的效果如下:
- 分發(fā)
onDispatchTouchEvent
- 返回 true:事件不會繼續(xù)向下傳遞第练,就此結(jié)束。
- 返回 false:事件不會繼續(xù)向下傳遞轻要,但會調(diào)用上層的 onTouchEvent 方法進行事件處理复旬。對 Activity 來說沒有上層所以不會事件傳遞就此結(jié)束;對 ViewGroup冲泥、View 來說就是交由上層的 onTouchEvent 進行處理。
- 返回 super:事件繼續(xù)向下層傳遞,交由下層去分發(fā)處理凡恍。
- 攔截
onInterceptedTouchEvent
(這個是 ViewGroup 特有的方法志秃,只有當(dāng)dispatchTouchEvent
返回 super 的時候才會調(diào)用這個方法)- 返回 true:表示對事件進行攔截,接下來事件將交由自身的
onTouchEvent
進行處理嚼酝。 - 返回 false 或者返回 super:不對事件進行攔截浮还,事件將交由下層
dispatchTouchEvent
進行分發(fā)。
- 返回 true:表示對事件進行攔截,接下來事件將交由自身的
- 消費
onTouchEvent
- 返回 true:表示事件被處理了闽巩,事件不會再進行傳遞钧舌,就此結(jié)束。
- 返回 false 或者返回 super:表示自己沒有完全處理這個事件涎跨,事件會繼續(xù)傳遞到上層的
onTouchEvent
進行處理洼冻。
文字表述可能不夠直觀,那我們畫個流程圖來看一下:
好了隅很,到此為止已經(jīng)帶大家過了一遍整體的流程撞牢,建立了一個初步的認(rèn)識。
其實看到這個結(jié)果一點都不驚訝叔营,畢竟這個流程跟我們平時工作中的任務(wù)分派簡直太像了屋彪,Android 中觸摸事件的傳遞也是從上層一層一層向下傳遞,每一層可以決定自己如何去處理事件以及給上層一個反饋绒尊。工作中不也是這樣子嗎畜挥,任務(wù)從上層分發(fā)下來,下層根據(jù)自己的情況決定是繼續(xù)分發(fā)還是自己處理婴谱,最后不管哪層去處理任務(wù)都要給上層一個反饋砰嘁。
一直認(rèn)為計算機的思維,或者說是編程的思維都是從生活中來的勘究。所以說嘛編程不僅僅是編程啊矮湘,我們也要聯(lián)系生活,相互啟發(fā)口糕,朝著優(yōu)秀的方向努力缅阳。相信自己,你是優(yōu)秀的>懊琛J臁!