一、用户点击屏幕,事件到activity相应的过程
1、手指触摸屏幕时,产生了触摸信息。这个触摸信息由屏幕这个硬件产生,被系统底层驱动获取,交给Android的输入系统服务:InputManagerService,也就是IMS
2、IMS会对这个触摸信息进行处理,通过WMS找到要分发的window,随后发送给对应的viewRootImpl。
3、android的view管理是以window为单位的,每个window对应一个view树。
4、Window机制不仅管理着view的显示,也负责view的事件分发。
5、每一棵view树都有一个根,叫做ViewRootImpl ,负责管理这整一棵view树的绘制、事件分发等。
6、App会有多个view树,activity布局就是一个view树、应用的悬浮窗也是一个view树、dialog界面也是一个view树,等等...
7、android中view的绘制和事件分发,都是以view树为单位。每一棵view树,则为一个window
8、系统服务WindowManagerService,管理界面的显示就是以window为单位,也可以说是以view树为单位。
view树是由viewRootImpl来负责管理的,所以可以说,wms管理的是viewRootImpl
9、wms是运行在系统服务进程的,负责管理所有应用的window。app 与wms的通信必须通过Binder进行跨进程通信。
10、每个viewRootImpl在wms中都有一个windowState对应,wms可以通过windowState找到对应的viewRootImpl进行管理。
11、事件分发并不是由Activity驱动的,而是由系统服务驱动viewRootImpl来进行分发
1、IMS从系统底层接收到事件之后,会从WMS中获取window信息,并将事件信息发送给对应的viewRootImpl;
2、viewRootImpl接收到事件信息,封装成motionEvent对象后,发送给管理的view;
3、view会根据自身的类型,对事件进行分发还是自己处理;
4、顶层viewGroup一般是DecorView,DecorView会根据自身callBack的情况,选择调用callBack或者调用父类ViewGroup的方法
5、不管顶层viewGroup的类型,最终都会到达ViewGroup对事件进行分发。
二、View 事件分发流程
核心三个方法:dispatchTouchEvent 、onInterceptTouchEvent、onTouchEvent
Window.CallBack接口中包含了 dispatchTouchEvent 和 onTouchEvent 方法,Activity和Dialog都实现了Window.CallBack接口,因此都实现了该方法。
1、dispatchTouchEvent
1.1、事件分发的核心方法,事件分发的逻辑都是在这个方法中实现;
1.2、View、以及 ViewGroup、其他的实现类都重写了该方法;
1.3、如果成功处理则返回true,处理失败则返回false,表示事件没有被处理。
1.4、在view的相关类中,该方法的主要作用是消费触摸事件。
1.5、在viewGroup相关类中,该方法的主要作用是把事件分发到该viewGroup所拥有的子view,如果子view没有处理则自己处理;
2、onInterceptTouchEvent
2.1、方法只存在于viewGroup中,当一个事件需要被分发到子view时,viewGroup会调用此方法检查是否要进行拦截。如果拦截则自己处理,而如果不拦截才会调用子view的 dispatchTouchEvent 方法分发事件;
2.2、方法返回true表示拦截事件,返回false表示不拦截;
3、onTouchEvent
3.1、方法返回true表示消费事件,返回false表示不消费事件;
3.2、viewGroup分发事件时,如果没有一个子view消费事件,那么会调用viewGroup自身的onTouchEvent方法来处理事件。
3.3、View的dispatchTouchEvent方法中,先调用onTouchListener判断是否消费;如果onTouchListener没有消费事件,才会调用onTouchEvent来处理事件;
三、规则&流程
一个触控点的事件序列只能给一个view消费,除非发生异常情况;
四、onTouch 和onTouchEvent 的区别
onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。
假如onTouch方法返回false,会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。
内置诸如click事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发。
五、事件是先到DecorView还是先到Window
ViewRootImpl -> DecorView -> Activity -> PhoneWindow -> DecorView -> ViewGroup
六、点击事件被拦截,但是相传到下面的view,如何操作?
在子View中
getParent().requestDisallowInterceptTouchEvent(true);
七、滑动冲突的解决方案
1、外部拦截法
外部拦截法是指点击事件都事先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截,这种方法比较符合点击事件的分发机制,外部拦截伐需要重写父容器的onInterceptTouchEvent方法在内部做相应的拦截即可;
2、内部拦截法
内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器处理,这种方法和Android的事件分发机制不一致需要配合requestDisallowInterceptTouchEvent方法才能正常工作;
八、同时对父 View 和子 View 设置点击方法,优先响应哪个
优先响应子 view;
如果先响应父 view,那么子 view 将永远无法响应;
九、requestDisallowInterceptTouchEvent的调用
一个手势的操作,会经历down,move,up等;
子view调用requestDisallowInterceptTouchEvent(true)是必须获取到点击事件,如果在down的时候调用了此方法,接下来的move,up都会传到子view上了,如果是在子view的move方法中调用的话,那么父view在move的过程中能将事件传递给子view就即可。