目录
事件分发机制
指当屏幕上发生MotionEvent时,找到一个合适的view来处理消耗这个事件的过程。
MotionEvent
当用户触摸屏幕时会产生一个事件,将这个事件封装为MotionEvent,常见的MotionEvent有:
DOWN
:首次按下屏幕时触发
MOVE
:在屏幕上滑动时触发,可多次触发
UP
:离开屏幕时触发
CANCEL
:当事件中途被上层拦截时触发
三个方法
三个主要的方法来完成这个流程:
dispatchTouchEvent()
onIntercepterTouchEvent()
onTouchEvent()
方法 |
dispatchTouchEvent() |
onIntercepterTouchEvent() |
onTouchEvent() |
作用 |
分发事件 |
拦截事件 |
处理消费事件 |
调用时刻 |
事件被传入view时 |
ViewGroup中的dispatchTouchEvent(); 当一个事件将要被分发给子View时调用 |
dispatchTouchEvent()调用; 先调用onTouchListener判断是否消费,不消费才调用onTouchEvent() |
存在位置 |
View, ViewGroup,Activity等 |
ViewGroup |
View, ViewGroup(默认没有重写) |
返回 |
true:分发成功 false:分发失败 |
true:将事件拦截 false:未拦截事件 |
true:处理事件 false:未处理事件 |
使用 |
可复写 |
若有需要去拦截事件,需要去复写该方法 |
复写该方法去消费事件 |
流程
触摸事件发生后会交给Android的输入系统服务InputManagerService(IMS),IMS会从 WMS中获取要发送的window,然后将事件发送给对应的viewRootImpl(viewRootImpl用来管理整个view树的绘制,事件分发等)。
事件分发的开始:
- viewRootImpl接收到事件信息,封装成motionEvent对象后,发送给管理的view;
- view会根据自身的类型,对事件进行分发还是自己处理,如果是一个view则处理,如果是一个viewGroup则根据viewGroup的类型进行分发;
- activity和dialog等布局界面的顶层viewGroup一般是DecorView,DecorView在dispatchTouchEvent()会根据自身callBack的情况,选择调用callBack(activity、dialog实现了windowCallBack接口)或者调用父类ViewGroup的事件分发方法。
事件分发流程:
-
一个触控点的事件序列只能给一个view消费
。
-
actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null
一旦拦截一个down事件或者没有子view消费事,后续对应的move、up等都不会再判断是否拦截。
- 一旦拦截一个事件交由自己的ontouchEvent处理,后续该事件也由此viewGroup处理;
- 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一件序列中的其他事件都不会再交给它处理,将重新交由它的父元素去处理。
- 如果一个view消耗了DOWN事件却在接下来的MOVE或UP事件返回了false,那么此事件不会给上层的viewGroup处理,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。
- 当viewGroup拦截子view的move或up事件之后,会将当前事件改为cancel事件并发送给子view。下一次的事件才会交由viewGroup消费。
-
子view可以请求viewGroup不要拦截down之外的事件。通过requestDisallowInterupt方法强制要求viewGroup不要拦截事件,viewGroup中会设置一个
FLAG_DISALLOW_INTERCEPT
标志表示不拦截事件。但这只针对于down事件之外的事件,因为down事件会对该标志重置。
- 如果外部注册了onTouchListener,onTouch优于onTouchEvent调用,如果返回true则表示消费了该事件,将不会调用onTouchEvent。
- View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。View的longClickable属性默认为false,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认为false。
- onTouchEvent,处理ACTION_UP事件时,会触发performClick方法,如果这个view设置了onClickListenter就会调用onClick方法。