参考
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
一、viewgroup向子元素传递事件
当一个MotionEvent产生后,系统要把这个事件传递给具体的view,这个传递过程就是分发。传递过程遵循如下顺序:Activity>Window>View,即事件总是先传递给Activity,然后到PhoneWindow,最后再传递给顶级view(一般是个viewgroup)。
顶级view收到事件后,会调用它的dispatchTouchEvent,伪代码如下:
<pre>
public boolean dispatchTouchEvent(MotionEvent event){
boolean consume = false;
if(onInterceptTouchEvent(event)){
consume = onTouchEvent(event);
}else{
consume = child.dispatchTouchEvent(event);
}
return consume;
}
</pre>
解释一下:
如果viewgroup的onInterceptTouchEvent事件返回true,就会去执行自己的onTouchEvent事件,表示自己拦截处理;相反的,如果onInterceptTouchEvent返回false,则表示自己不处理,dispatchTouchEvent交给子元素继续本过程,如此反复直到事件被处理。
同一个事件序列是指从手指接触屏幕那一刻起,到手指离开屏幕那一刻结束,这个过程中产生的一系列事件,以down事件开始,中间含有数量不定的move事件,最终以up事件结束。
下面是dispatchTouchEvent具体代码:
<pre>
final boolean intercepted;
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null){
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if(!disallowIntercept){
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
}else{
intercepted = false;
}else{
intercepted = true;
}
</pre>
也就是说,viewgroup会在两种情况下判断是否要拦截当前事件:事件类型为ACTION_DOWN或者mFirstTouchTarget != null。从后面逻辑看出,当事件由viewgroup子元素成功处理时,mFirstTouchTarget会指向这个子元素。也就是说,如果自己处理了,那么这个条件就不会满足。后续的ACTION_MOVE和ACTION_UP事件因为这个条件不满足,就不会再去执行onInterceptTouchEvent。
结论就是,一个事件序列只能被一个view拦截并消耗。当一个view决定拦截一个事件后,系统就会把同一个事件序列中的其他方法都交给它来处理,不会再调用onInterceptTouchEvent事件去询问它是否要拦截。
ViewGroup默认不拦截任何事件,其onInterceptTouchEvent方法默认返回false.
View没有onInterceptTouchEvent事件,一旦有点击事件传递给它,onTouchEvent就会被调用。onTouchEvent默认会消耗掉事件(返回true),enable属性不会影响这个默认值,除非clickable和longClickable同时为false.
二、view处理事件
view会先判断有没有设置onTouchListener,如果onTouchListener中的onTouch事件返回了true,那么view自己的onTouchEvent就不会被调用。这样的好处是方便在外界处理点击事件。
在onTouchEvent中会处理点击事件。当ACTION_UP发生时,如果view设置了onClickListener,会调用其onClick方法。这里也可以看出,onClickListener优先级最低,处于事件传递的尾端。
如果view的onTouchEvent事件返回false,那么同一事件序列的其他事件也不会再交给它处理,事件会重新交给父元素的onTouchEvent。点击事件就像一个难题,经过一系列事件分发过程,领导指定了一个程序员view去处理。但是这个程序员搞不定,在onTouchEvent中返回了false.那上级就不敢再把后续事件交给这个程序员处理了。事件会重新交给上一级,如果上一级也搞不定,会一层层往上抛,最终返回给activity的onTouchEvent来处理。
看一个例子:
<pre>
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "onClick execute");
}
});
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "onTouch execute, action " + event.getAction());
return false;
}
});
</pre>
点击的时候,会输出什么呢:
onTouch execute action 0
onTouch exxecute action 1
onClick execute
根据上面的结论,很容易分析到OnTouchListener会最先执行,因为onTouch返回了false,所以onTouchEvent会接着执行。如果有onClickListener,又会去执行。那么如果onTouch返回true呢,事件就被消耗了,显然onClick方法就不会去执行了。