Touch事件分发机制
纯属个人理解,不适合别人阅读学习;适合理解的人指出我的一些错误理解和讨论;
一个完整的Touch时间,包括一个down事件,多个move事件,一个up事件,偶尔还会有cancel事件;
Touch事件的一般传递流程Activity->window(唯一实现类PhoneWindow)->顶级View(DecorView)->VIewGroup->View
首先从dispatchTouchEvent中开始分发,中间会碰到onInterceptTouchEvent来判断是否要拦截事件,默认是不拦截;如果拦截的话,会将事件传递给onTouchEvent方法中去处理,如果不拦截,则会将事件继续下发在下级View或者ViewGroup的dispatchTouchEvent中去,这样重复上面的操作,一直到View的dispatchTouchEvent,View是没有拦截方法的,所以直接到了View的onTouchEvent事件中,如果该方法返回true则该事件消耗掉,否则就是将事件往上传给父控件的onTouchEvent中去处理;
在这中间,如果设置了onTouchListener的话,会先执行onTouch方法,如果该方法返回true,就没有了后来的onTouchEvent的事了,只有返回false才有机会;
梳理了代码流程后:
一个Touch事件发出,首先触发Activity的dispatchTouchEvent方法,在内部,如果当前是down事件,则触发屏保方法(onUserInteraction()方法,在触摸home,back,menu时触发),然后调用
return getWindow.superDispatchTouchEvent(ev)
|
return mDecor.superDispatchEvent(ev)
|
return super.dispacthTouchEvent(ev)
mDecor是顶层View,对应着FrameLayout,他的super就是ViewGroup;也就是调用ViewGroup的dispatchTouchEvent方法;
在ViewGroup中,会有一个拦截判断:disallowIntercept||!onInterceptTouchEvent(ev)
如果不拦截就往下走,拦截就跳过这个判断条件;
如果不拦截往下走会遍历ViewGroup的所有子View,找到被点击的那个View,调用child.dispatchTouchEvent;如果返回true,则整体返回true,如果返回false,则跳出拦截开始的这个判断条件,和上面拦截走同一条路;调用super.dispatchTouchEvent方法(ViewGroup的super就是View,也就是调用View的dispatchTouchEvent,注意:这里的View不同于上面的子View,上面的子View是位于ViewGroup上,而这个里的View则是ViewGroup的父类,通过调用父类View的dispatchTouchEvent来间接调用ViewGroup的onTouchEvent方法,其实你会发现在ViewGroup中是没有onTouchEvent这个方法,因为他是继承自View,所以才有了这个方法的使用权,本质上这个onTouchEvent还是父View中的方法)
既然两条路都走到了View中,那就继续View的dispatchTouchEvent
在View的dispatchTouchEvent中,有三个判断条件:
1、onTouchListener不为空
2、View可用enable
3、onTouch返回true
三个都成立的时候:return onTouchEvent(ev);
如果是从ViewGroup拦截过来的,则最终ViewGroup的dispatchTouchEvent返回onTouchEvent同等结果,如果是从child.dispatchTouchEvent过来的则返回onTouchEvent同等结果
这里之前我一直有个问题就是:Touch事件从Activity到ViewGroup再到View,如果一直默认执行则会调用View的onTouchEvent,如果View的onTouchEvent返回false则调用ViewGroup的onTouchEvent,我一直比较好奇是怎么调用到父类的;
这里就可以看出来,当View的onTouchEvent返回false时,就会在ViewGroup的dispatchTouchEvent继续往下走,然后调用了super.dispatchTouchEvent,这样就到了View的dispatchTouchEvent中,就会调用到ViewGroup的onTouchEvent;
这里贴一副我理解的图:
当一个事件序列下发时,如果down被View消耗掉,然后ViewGroup拦截了move事件,则这个move事件会变成cancel事件继续下发到消耗down事件的View,而且不会在回传;下一个move事件才会到达ViewGroup的onTouchEvent。
本篇文章完全基于Android事件分发机制详解:史上最全面、最易懂和可能是讲解Android事件分发最好的文章所理解的,不太适合别人阅读,如有需求可阅读这两篇。
Android事件分发机制详解:史上最全面、最易懂
可能是讲解Android事件分发最好的文章