前言:本篇博文主要分析關(guān)于onTouchEvent,dispatchTouchEvent和onInterceptTouchEvent這三個(gè)方法的作用和它們之間的關(guān)系,通過(guò)流程圖來(lái)解釋觸摸事件(MotionEvent)是怎么在三個(gè)方法中傳遞的;
為了了解三個(gè)方法之間的關(guān)系,我們通過(guò)一個(gè)demo來(lái)分析它們之間的關(guān)系,demo主要內(nèi)容為在一個(gè)activity上設(shè)置兩個(gè)布局控件,一個(gè)為L(zhǎng)inearLayout,一個(gè)是TextView,兩個(gè)控件都自定義重寫(xiě)了它們的onTouchEvent、dispatchTouchEvent和onInterceptTouchEvent方法,并用logcat打印出各個(gè)方法相應(yīng)的log標(biāo)記,通過(guò)觸摸控件后log的打印順序就可知道他們的調(diào)用順序了,通過(guò)調(diào)用順序去分析事件的傳遞流程,如下圖所示為activity的視圖
其中紅色區(qū)域?yàn)門(mén)extview的視圖,底下綠色區(qū)域?yàn)長(zhǎng)inearLayout的視圖,最底下藍(lán)色區(qū)域?yàn)閍ctivity的視圖;下面貼上代碼:
public class GestureDemo extends Activity {
public final static String TAG = "GestureDemo";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"Activity onTouchEvent. action="+getAction(event));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"Activity dispatchTouchEvent. action="+getAction(ev));
return super.dispatchTouchEvent(ev);
}
}
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context,attrs,defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(GestureDemo.TAG,"MyLinearLayout dispatchTouchEvent. action="+GestureDemo.getAction(ev));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(GestureDemo.TAG,"MyLinearLayout onInterceptTouchEvent. action="+GestureDemo.getAction(ev));
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(GestureDemo.TAG,"MyLinearLayout onTouchEvent. action="+GestureDemo.getAction(event));
return super.onTouchEvent(event);
}
}
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs){
super(context,attrs);
}
MyTextView(Context context, AttributeSet attrs, int defStyleAttr){
super(context,attrs,defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(GestureDemo.TAG,"MyTextView dispatchTouchEvent. action="+GestureDemo.getAction(event));
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(GestureDemo.TAG,"MyTextView onTouchEvent. action="+GestureDemo.getAction(event));
return super.onTouchEvent(event);
}
}
注: 每一次屏幕觸摸后的手勢(shì)事件都是連續(xù)地由一個(gè)down事件、若干move事件和一個(gè)up事件組成,每次都必須是down事件先發(fā)生,最后一個(gè)是up事件;
- 觸摸事件:每一個(gè)down事件、move事件或up事件都為一個(gè)獨(dú)立的觸摸事件;
- 手勢(shì)事件:由一系列連續(xù)的觸摸事件組成的一個(gè)完整的手勢(shì),比如單擊手勢(shì)事件為down-up, 滑動(dòng)事件為down-move…move-up;
下面通過(guò)逐個(gè)改變各個(gè)方法的返回值,分析它們之間的傳遞圖;
一、 關(guān)于onTouchEvent事件
1. 所有方法都返回父類的方法,即MotionEvent沒(méi)有被子控件消耗掉,最終只有在activity的onTouchEvent消失掉(系統(tǒng)默認(rèn)傳遞流程);
2. Activity的onTouchEvent方法返回true,其事件傳遞流程和上一個(gè)相同
3. MyLinearLayout的onTouchEvent返回true
4. MyTextView的onTouchEvent返回true;
總結(jié):以上四張圖中,只改動(dòng)onTouchEvent的返回值,其他方法都調(diào)用父類方法,也就是傳遞流程都為系統(tǒng)默認(rèn)的流程,沒(méi)有人為干預(yù),圖1即是默認(rèn)的系統(tǒng)流程,而圖2,3,4通過(guò)改變onTouchEvent的返回值為true來(lái)終止事件的繼續(xù)傳遞;從圖中可知,當(dāng)屏幕觸摸發(fā)生時(shí),down事件每次都是由系統(tǒng)先調(diào)用最底層的activity的dispatchTouchEvent事件,然后繼續(xù)往上傳遞到最頂層控件,然后由onTouchEvent往下傳遞,按照默認(rèn)傳遞順序,如果哪個(gè)控件返回true,事件就在那個(gè)控件停止,即被這個(gè)控件消耗掉,如:activity和MyLinearLayout的onTouchEvent都返回true,那么事件只傳遞到MyLinearLayout的onTouchEvent,因?yàn)槭录冉?jīng)過(guò)MyLinearLayout的onTouchEvent,先被它截住了;而后續(xù)的move、up事件則會(huì)根據(jù)上個(gè)down事件最終在哪個(gè)控件被消耗掉,然后按照默認(rèn)系統(tǒng)流程傳遞到該控件的dispatchTouchEvent后直接傳遞給自己的onTouchEvent,不會(huì)再往上層控件傳遞;
二、關(guān)于dispatchTouchEvent
1. MyLinearLayout的dispatchTouchEvent返回true
2. MyTextView的dispatchTouchEvent返回true
3. MyLinearLayout的dispatchTouchEvent返回false
4. MyTextView的dispatchTouchEvent返回false
總結(jié):dispatchTouchEvent從方法名大約就可以知道其功能是分發(fā)觸摸事件,該方法會(huì)根據(jù)它的返回值來(lái)決定事件要往哪個(gè)方向傳遞:
(1)若返回父類方法,則交由系統(tǒng)來(lái)決定,即為系統(tǒng)默認(rèn)方向繼續(xù)傳遞給上層控件的方法;
(2)若返回true,則不再繼續(xù)傳遞,傳遞到這個(gè)方法為止,即這個(gè)方法消耗了事件;
(3)若返回false,事件傳遞到該方法后,事件退回到底下的父控件,繼續(xù)往系統(tǒng)默認(rèn)方向傳遞;如圖8所示,MyTextView的dispatchTouchEvent返回false,事件退回到MyLinearLayout的onInterceptTouchEvent,繼續(xù)往MyLinearLayout的onTouchEvent傳遞,即相當(dāng)于以MyLinearLayout為最頂層控件;
三、關(guān)于onInterceptTouchEvent
1. MyLinearLayout的onInterceptTouchEvent返回true
總結(jié):該方法只有ViewGroup的控件才會(huì)有,其父類的方法直接返回false,所以返回false就為圖1中按默認(rèn)方向傳遞了;該方法的作用就是攔截觸摸事件,如果返回true,就是在當(dāng)前控件攔截住觸摸事件,傳遞給自己的onTouchEvent方法,再繼續(xù)往底下控件傳遞給onTouchEvent,如果返回false或父類方法,就傳遞給它上面控件的dispatchTouchEvent;