前言:本篇博文主要分析關(guān)于onTouchEvent,dispatchTouchEvent和onInterceptTouchEvent這三個方法的作用和它們之間的關(guān)系,通過流程圖來解釋觸摸事件(MotionEvent)是怎么在三個方法中傳遞的漩怎;
為了了解三個方法之間的關(guān)系,我們通過一個demo來分析它們之間的關(guān)系,demo主要內(nèi)容為在一個activity上設置兩個布局控件,一個為LinearLayout左医,一個是TextView,兩個控件都自定義重寫了它們的onTouchEvent同木、dispatchTouchEvent和onInterceptTouchEvent方法浮梢,并用logcat打印出各個方法相應的log標記,通過觸摸控件后log的打印順序就可知道他們的調(diào)用順序了彤路,通過調(diào)用順序去分析事件的傳遞流程秕硝,如下圖所示為activity的視圖
其中紅色區(qū)域為Textview的視圖,底下綠色區(qū)域為LinearLayout的視圖洲尊,最底下藍色區(qū)域為activity的視圖远豺;下面貼上代碼:
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);
}
}
注: 每一次屏幕觸摸后的手勢事件都是連續(xù)地由一個down事件、若干move事件和一個up事件組成坞嘀,每次都必須是down事件先發(fā)生躯护,最后一個是up事件;
- 觸摸事件:每一個down事件姆吭、move事件或up事件都為一個獨立的觸摸事件榛做;
- 手勢事件:由一系列連續(xù)的觸摸事件組成的一個完整的手勢唁盏,比如單擊手勢事件為down-up, 滑動事件為down-move…move-up内狸;
下面通過逐個改變各個方法的返回值,分析它們之間的傳遞圖厘擂;
一昆淡、 關(guān)于onTouchEvent事件
1. 所有方法都返回父類的方法,即MotionEvent沒有被子控件消耗掉刽严,最終只有在activity的onTouchEvent消失掉(系統(tǒng)默認傳遞流程)昂灵;
2. Activity的onTouchEvent方法返回true避凝,其事件傳遞流程和上一個相同
3. MyLinearLayout的onTouchEvent返回true
4. MyTextView的onTouchEvent返回true;
總結(jié):以上四張圖中眨补,只改動onTouchEvent的返回值管削,其他方法都調(diào)用父類方法,也就是傳遞流程都為系統(tǒng)默認的流程撑螺,沒有人為干預含思,圖1即是默認的系統(tǒng)流程,而圖2,3,4通過改變onTouchEvent的返回值為true來終止事件的繼續(xù)傳遞甘晤;從圖中可知含潘,當屏幕觸摸發(fā)生時,down事件每次都是由系統(tǒng)先調(diào)用最底層的activity的dispatchTouchEvent事件线婚,然后繼續(xù)往上傳遞到最頂層控件遏弱,然后由onTouchEvent往下傳遞,按照默認傳遞順序塞弊,如果哪個控件返回true漱逸,事件就在那個控件停止,即被這個控件消耗掉居砖,如:activity和MyLinearLayout的onTouchEvent都返回true虹脯,那么事件只傳遞到MyLinearLayout的onTouchEvent,因為事件先經(jīng)過MyLinearLayout的onTouchEvent奏候,先被它截住了循集;而后續(xù)的move、up事件則會根據(jù)上個down事件最終在哪個控件被消耗掉蔗草,然后按照默認系統(tǒng)流程傳遞到該控件的dispatchTouchEvent后直接傳遞給自己的onTouchEvent咒彤,不會再往上層控件傳遞;
二咒精、關(guān)于dispatchTouchEvent
1. MyLinearLayout的dispatchTouchEvent返回true
2. MyTextView的dispatchTouchEvent返回true
3. MyLinearLayout的dispatchTouchEvent返回false
4. MyTextView的dispatchTouchEvent返回false
總結(jié):dispatchTouchEvent從方法名大約就可以知道其功能是分發(fā)觸摸事件镶柱,該方法會根據(jù)它的返回值來決定事件要往哪個方向傳遞:
(1)若返回父類方法,則交由系統(tǒng)來決定模叙,即為系統(tǒng)默認方向繼續(xù)傳遞給上層控件的方法歇拆;
(2)若返回true,則不再繼續(xù)傳遞范咨,傳遞到這個方法為止故觅,即這個方法消耗了事件;
(3)若返回false渠啊,事件傳遞到該方法后输吏,事件退回到底下的父控件,繼續(xù)往系統(tǒng)默認方向傳遞替蛉;如圖8所示贯溅,MyTextView的dispatchTouchEvent返回false拄氯,事件退回到MyLinearLayout的onInterceptTouchEvent,繼續(xù)往MyLinearLayout的onTouchEvent傳遞它浅,即相當于以MyLinearLayout為最頂層控件译柏;
三、關(guān)于onInterceptTouchEvent
1. MyLinearLayout的onInterceptTouchEvent返回true
總結(jié):該方法只有ViewGroup的控件才會有姐霍,其父類的方法直接返回false艇纺,所以返回false就為圖1中按默認方向傳遞了;該方法的作用就是攔截觸摸事件邮弹,如果返回true黔衡,就是在當前控件攔截住觸摸事件,傳遞給自己的onTouchEvent方法腌乡,再繼續(xù)往底下控件傳遞給onTouchEvent盟劫,如果返回false或父類方法,就傳遞給它上面控件的dispatchTouchEvent与纽;