事件分发机制
这个系列准备分享一些自己有独特见解的知识点。
从一个故事讲起
用户点击屏幕会触发一个MotionEvent,它就是我们要进行分发的事件。事件分发的最终目标是希望有一个View能够给他返回一个True,即进行了处理(消耗该事件)。在这里,我们把事件分发的过程,比喻成一个大学生event(MotionEvent)想要在大学(Activity)中通过加入社团(ViewGroup),来寻找女盆友(View),从而找到真爱(True)的故事,我们将会以上帝视角来监视这个过程。
部长开会讨论的怎样分配event的过程相当于事件分发中的:
dispatchTouchEvent()
如果妹纸部长不让event进入部门里,就是不允许事件从当前ViewGroup传递到子View(或者子ViewGroup),也就是拦截了event(虽然拦截但是未必处理(未必消耗)),这个过程相当于:
onInterceptTouchEvent()
event找到了目标,目标可能是部长,可能是部员,也可能是其他的部门部长或者部员,这个目标可能是View,也可能是ViewGroup。反正在这个故事中,这个目标最终做出了回应,这个过程相当于:
onTouchEvent()
所以,事件分发的核心过程我们可以用这样一段伪代码进行表示:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if ( onInterceptTouchEvent(ev) ){
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
事件在ViewGroup中进行分发,如果没有被消耗,则通过onInterceptTouchEvent()判断是否需要拦截,如果拦截则交给本ViewGroup的onTouchEvent()进行处理,如果不拦截则交给子View(或ViewGroup)继续重复dispatchTouchEvent()的过程进行分发,从外向内,以此类推。
以下:
1. 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。
2. 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。
3. 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
4. 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。
5. OnTouchListener优先于onTouchEvent()对事件进行消费。
此外,我们在之前的故事中特别地标注了一些“重要情节”,现在来进行解释:
总结下来就是:
1. 一旦 ViewGroup决定拦截,一个事件只能由他处理并且onInterceptTouchEvent不会再调用。
2. 如果View只消耗ACTION_DOWN,事件会消失(不会被parent处理),最终Activity处理。
3. View如果不消耗ACTION_DOWN,其他事件也不处理。重新交给父元素,即调用父元素onTouchEvent。
4. viewgroup默认不拦截
5. View没有onInterceptTouchEvent
6. 除非不可点击(clickable、longClickable都为false),View的onTouchEvent()默认返回true,甚至enable都影响不了。
7. onTouchListener优先级高于onTouchEvent,如果onTouchListener返回True,onTouchEvent不执行
具体的源码今后再分析。