转载请注明出处:http://blog.csdn.net/u013220682/article/details/50547022
在平常的开发中,我们经常会遇到点击,滑动之类的事件。有时候不同的view之间也存在各种滑动冲突。比如布局的内外两层都能滑动的话,那么就会出现冲突了。这个时候如果我们对Android中的事件分发机制不熟悉的话,解决这类问题就比较棘手了。所以对Android源码的分析后得出的一些总结供大家考。
点击事件的分发过程由三个很重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。我先将这三个方法大体的介绍一下。
public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的disPatchTouchEvent方法的影响,表示是否消耗当前事件。
public boolean onInterceptTouchEvent(MotionEvent event)
这个方法是在disPatchTouchEvent方法中调用的,用来拦截某个事件的。如果当前View拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回的结果表示是否拦截当前事件。
public boolean onTouchEvent(MotionEvent event)
在diaPatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗掉当前事件(true表示消耗,false表示不消耗),如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。
上述三个方法中有什么区别和关系呢?我在这用下面一段伪代码表示估计会更清晰:
- public boolean dispatchTouchEvent(MotionEvent ev) {
- boolean consume = false;
- if(onInterceptTouchEvent(ev)){
- consume = onTouchEvent(ev);
- } else {
- consume = child.dispatchTouchEvent(ev);
- }
- return consume;
- }
通过上面的伪代码大家可能对点击事件的传递规则有了更清楚的认识,即:对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true表示它要拦截此事件,接着这个事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果这个ViewGroup的onInterceptTouchEvent方法返回false,就表示它不拦截此事件,这是当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件被最终处理。
对事件传递的过程有了整体的认识后,大家可能有时候会对onTouch,onTouchEvent,onClick产生疑惑,他们之间到底是什么区别呢?其实从分析源码会发现,这三个有一个优先级的区别的。
当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调,这时事件如何处理还要看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被回调;如果返回true,那么onTouchEvent方法将不会被回调。如果View设置了onClickListener,那么在onTouchEvent方法中,会调用它的onClick方法。
由此可见,这三者的优先级是:onTouch > onTouchEvent > onClick。
针对上面的分析,我对Android中的事件分发机制做个总结如下:
1. 事件传递的顺序遵循:Activity -> Window -> View,如果View没有消耗掉事件,则会直接传递给Activity来处理。
2. 某个View一旦决定拦截,那么这个事件序列都只能由它来处理,并且它的onInterceptTouchEvent不会再调用。
3. View的onTouchEvent默认都会消耗掉事件(当该方法返回true),除非它是不可点击的(clickable和longClickable同时为false)。并且View的longClickable默认为false,clickable属性要分情况,比如Button默认为true,TextView默认为false。
4. 正常情况下,一个事件序列只能被一个View拦截且消耗。一旦一个元素拦截消耗了某件事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理
5. 如果一个View不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一事件序列中的其他事件都不会交给它来处理,并且事件重新交给他的父元素去处理,及父元素的onTouchEvent方法会被调用
6. 如果View不消耗除了ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent方法并不会被调用,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity。
7. ViewGroup默认不拦截任何事情,View没有onInterceptTouchEvent方法。
8. View是否是可用的(enable属性)不影响onTouchEvent的默认返回值。
9. onClick发生的前提是当前View是可点击的,并且它收到了down和up事件。
我写这篇博客只是对Android中的事件分发机制做了一个初步的总结,很多大神(郭林)已经对Android的事件分发做了详尽的分析,所以这里我就没有贴出源码来分析了,仅供大家参考,谢谢!