参考http://www.jianshu.com/p/2be492c1df96
onInterceptTouchEvent:
- 事件拦截方法,只有在ViewGrop中存在,因为事件都会先传递到最顶层的parent,如果它没有做处理,该事件(dowm,up等事件)就会传递到下一层,下一层可能是view,也可能是viewgrop,如果是view则会进入它的onTouchEvent,如果是viewGroup则会进入它的onInterceptTouchEvent,以这样的规则往下传递。
- 而且这个事件只能是一个一个处理:例如如果拦截了dowm事件,后面的move和up事件不会一起被拦截(就算底层的view处理了dowm事件,接下来的move事件还会传递到最顶层viewGroup的onInterceptTouchEvent中),这点与onTouchEvent不同!
onTouchEvent:
- 只存在view(图中的C)中,一旦拦截了dowm事件,那么后面的move和up事件将不会传递到parent(图中的A和B)的onTouchEvent中!但是:后面的事件还是会先传递到A和B的onInterceptTouchEvent方法中的,如果此时A在它的onInterceptTouchEvent中对move事件进行了拦截,则C就不会再收到move事件(比如scroll事件就需要这样做,当滑动超出一定距离后就认为是滑动,需要拦截)。所以,在编码中,我们也常常在view的dispatchTouchEvent方法中通过getParent().requestDisallowInterceptTouchEvent(true);来确保父控件无法拦截对应的事件(从博文中看出一定是成功的),如下:
getParent().requestDisallowInterceptTouchEvent(true); @Override public boolean dispatchTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: if (如果父容器需要这个事件) { getParent().requestDisallowInterceptTouchEvent(false); }//否则的话 就交给自己本身view的onTouchEvent自动处理了 break; case MotionEvent.ACTION_UP: break; default: break; } return super.dispatchTouchEvent(event); }
- (从博文中看出一定是成功的,所以这里应该没必要了)除了上面一个步骤,还需要确认父控件的onInterceptTouchEvent没有对事件进行拦截(父控件未拦截则不影响),否则子控件将无法收到该事件,如下:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { return false; } return true; }
- 如果最底层的view没有设置成clickable或者拦截这个事件,那么该事件将会继续往上传递到parent的onTouchEvent方法中。
如图,事件的处理实际上经历了一下一上两个过程,下是指A->B的onInterceptTouchEvent,上是指C->B->A的onTouchEvent,当然,任意一步的方法中返回true,都能阻止它继续传播
总结
- 事件最先由ViewGroup的onInterceptTouchEvent往下一层一层传递到View的onTouchEvent,如果没有拦截,则再会往上一层一层传递到ViewGroup的onTouchEvent。所以可理解为onInterceptTouchEvent自上而下传递,onTouchEvent自下而上传递。
- 一般如果某个View在onTouchEvent中拦截了dowm事件,如果没有必要,他的parent(ViewGroup)不会拦截接下来的事件。但是也有例外,这时候就会出现事件拦截(冲突),如子控件在onTouchEvent的dowm中返回了true,后面它想在move和up事件中也做一些处理,假设接下来它的parent在move事件中判断滑动大于某个值,它需要处理,并在onInterceptTouchEven的move中返回了true,此时这个事件将会变成一个CANCLE事件(文章中讲到的),view无法再处理了,而最后会传递到ViewGrop的onTouchEvent的move事件中。
- 虽然上面ViewGroup可以中途拦截View的事件,但是在一些必要的情况下我们可以通过getParent().requestDisallowInterceptTouchEvent(true);来确保父控件无法拦截对应的事件。(这个是否一定成功吗?)
- 其实dispatchTouchEvent才是整个事件处理的真正入口。如果我们需要监听整个Activity的事件并作出对应的处理(如监听点击非输入框的时候隐藏输入法),这样我们就可以重写Activity的事件分发方法dispatchTouchEvent,并判断当前获取焦点的view是否是EditText,不是则隐藏输入法。
- 注意与前面的两个方法不同,dispatchTouchEvent返回false则表示拦截!!!
事件传递的基本流程:
- 事件都是从Activity.dispatchTouchEvent()开始传递;
- 事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递;
- 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数;
- 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来;
- OnTouchListener优先于onTouchEvent()对事件进行消费。也就是如果View设置了onTouchListener,会先执行OnTouchListener的onTouch方法。如果该方法返回true,表示消费了。不会执行onTouchEvent了,如果onTouch返回false,就会继续执行onTouchEvent