关于ViewGroup事件分发机制的文章已经有很多了,推荐郭霖和鸿洋的两篇文章,
http://blog.csdn.net/guolin_blog/article/details/9153747
http://blog.csdn.net/lmj623565791/article/details/39102591
结合他们写的,自己简单总结一下,可能只适用个人。
流程
在上一篇文章中讲到,当你操作一个控件时,不管是按下、移动、抬起,在事件中途未被消费的情况下系统会按照View.dispatchTouchEvent -> OnTouchListener.onTouch -> View.onTouchEvent
分发下去。事实上,事件最先分发到的是ViewGroup
的dispatchTouchEvent
、然后分发到onInterceptTouchEvent
方法,而第二个方法顾名思义就是用来拦截消费事件的。代码如下,默认就是不拦截。
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
接下来看一下ViewGroup.dispatchTouchEvent
方法,源码比较长为了不占篇幅,可以直接在上面的文章看。观察源码可以发现,当按下
控件时事件会分发到onInterceptTouchEvent
方法中,而默认这个方法是返回false,然后系统通过坐标的计算得出当前按的控件是否是ViewGroup里面的子View,如果是,将事件分发给这个子View,也就是按照View.dispatchTouchEvent -> OnTouchListener.onTouch -> View.onTouchEvent
**,并且将这个子View赋给target记录下来,方便后面控件移动、抬起操作的识别。如果不是,则会有如下代码
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
可以看到如果点击的不是子View,则会把事件分发到super.dispatchTouchEvent
方法,而ViewGroup的父类正是View,所以可以这么理解,点击一个控件时,先在ViewGruop.dispatchTouchEvent
里面判断点击的是不是子View,是则按照View的事件分发机制分发下去,否则把ViewGroup本身作为一个View,按照View的事件分发机制分发下去。而常见的ViewGroup无非就是一些布局还有AdapterView等。
小结
- 如果ViewGroup可以找到子View,就将事件分发给子View,否则自己作为一个View分发事件。
- 通过重写
ViewGroup.onInterceptTouchEvent
方法可以实现对不同动作的事件的拦截,由ViewGroup自身处理事件。
分析两篇文章,我们一般都不需要重写ViewGroup和View的dispatchTouchEvent
方法和View.onTouchEvent
方法,程序员需要关心的是ViewGroup.onInterceptTouchEvent
和onTouch
,前者是实现ViewGroup对子View事件的拦截,后者是实现View分发流程时onTouch
方法对onTouchEvent
方法的拦截,而onTouchEvent
则是实现判断onLongClick
和onClick
等动作的逻辑的。
ViewGroup.onInterceptTouchEvent
默认返回false
,则点击到子View时会把事件分发给子View,onTouch
返回false
或者不存在OnTouchListener
时,则会把事件分发到onTouchEvent
,onLongClick
返回false
或者不存在OnLongClickListener
时,事件才有可能传递给onClick
方法