一直对view之间的手势事件传递机制模模糊糊,今天来好好理理,首先说说一些关于事件的一些基础知识
事件类型MotionEvent
ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL等等,并且每个事件都是以ACTION_DOWN开始ACTION_UP结束的
事件处理分类
事件分发
public boolean dispatchTouchEvent(MotionEvent ev)
只要你触摸了任何控件,就一定会调用该控件的dispatchTouchEvent方法
事件拦截
public boolean onInterceptTouchEvent(MotionEvent ev)
只有ViewGroup才有onInterceptTouchEvent方法,因为一个普通的View肯定是位于最深层的View,touch事件能够传到这里已经是最后一站了
事件响应
public boolean onTouchEvent(MotionEvent ev)
事件传递流程
(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。
(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。
(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
事件的层级传递
我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。
底层的View如何阻止父层的View截获touch事件
方法一:重写底层view的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
方法二:当用户按下的时候,我们告诉父组件,不要拦截我的事件(这个时候子组件是可以正常响应事件的),拿起之后就会告诉父组件可以阻止。
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
myView.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
myView.requestDisallowInterceptTouchEvent(false);
break;
}
}
最后在网上找了两张流程图,觉得表达的很好,也一起分享一下:
View不处理事件流程图
View处理事件流程图