事件分发机制是指系统对用户手指接触屏幕时所产生的一系列MotionEvent对象在View的层级中如何传递所制定的机制。
1.MotionEvent
MotionEvent是一个类,Android将输入事件都生成了 MotionEvent对象。主要有这几种事件类型:
类型 | 说明 |
ACTION_DOWN | 代表手指刚接触屏幕 |
ACTION_MOVE | 代表手指在屏幕上移动 |
ACTION_UP | 代表手指刚从屏幕松开 |
ACTION_CANCEL | 代表事件序列结束(一般为父view拦截事件) |
还有一些与多指触碰有关的这里不展开讨论。
2.事件序列
从手指触摸到屏幕开始直到手指离开屏幕所产生的一系列事件叫做同一事件序列。一个完整的事件序列,一般是以DOWNk开始,以UP结束,当然也有以CANCEL结束的,中间可能有多个MOVE。当一个View处理某个事件时,这个事件序列的后序事件都会直接传给此view。
3.分发流程
与分发过程有关的3个方法:
①dispatchTouchEvent(MotionEvent ev) ——进行事件分发
②onInterceptTouchEvent(MotionEvent ev)——进行事件拦截,viewGroup才拥有此方法,在dispatchTouchEvent()方法中调用
③onTouchEvent(MotionEvent ev)——进行点击事件处理,在dispatchTouchEvent()方法中调用
分发过程:
1.当事件产生时,首先传递给activity,我们知道activity加载时先添加phoneWindow,phoneWindow又会添加DecorView,所以DecorView为activity的根view,事件处理工作最后交给DecorView,而DecorView又将事件处理交给根viewGroup,调用根vewGroup的dispatchTouchEvent方法。
2.在此方法中,首先判断事件是否为down事件,是的话进行一些初始化工作(将上次事件序列的状态清空)。接着进行一个判断语句,如果事件为down事件或者mFirstTouchTarget不为空,则进行if语句,否则直接将拦截标志位设为true,表示viewGroup拦截此事件。mFirstTouchTarget表示的是上个事件的处理者,如果为空则表示没有处理者。所以这个判断语句表示如果是down事件,是一个事件序列的开始,应该询问子view是否处理,如果是其他事件并且mFirstTouchTarget为空,没人处理上个事件,则子view中没人处理,由viewGroup直接拦截处理。
3.在判断语句中,先得到disallowIntercept的值,这个标志位表示子view是否允许父view拦截除down之外事件,子view可通过调用requestDisallowInterceptTouchEvent来设置。为false的话表示不允许,直接将拦截标志位设为false。为true的话,表示允许,将调用viewGroup的onInterceptTouchEvent(),根据此方法的返回值设置拦截标志位,默认为false,有需要时需重写此方法。
4.如果viewGroup不拦截事件,则需遍历子view,判断子view是否能够接收事件,如能接收则交给子view。遍历子view为倒序遍历,则从最上层子view开始,判断条件为触摸点位置是否在子view范围内,或者子view是否在播放动画,均不符合则遍历下一个。最终如果由子view符合条件则调用子view的dispatchTouchEvent()方法,没有的话则调用super.dispatchTouchEvent()方法。viewGroup继承于View,所以调用的是View的dispatchTouchEvent方法。
5.在View的dispatchTouchEvent()方法中,如果onTouchListener不为空并且onTouch()方法返回true则表示事件被消费,不会再执行onTouchEvent()方法。在onTouchEvent()方法中,只要View的CLICKABLE和LONG_CLICKABLE有一个为true,那么OnTouchEvent()方法就会返回true消费此事件。如果View设置了点击事件OnClickListener,那么就会执行onClick方法。如果子view的dispatchTouchEvent()方法返回false,则会调用父view的onTouchEvent()方法。