最近在深入学习事件分发机制,一直被虐终于学有所成,特意把学后的总结记录下来,方便日后复习,也希望能帮助更多在事件分发上迷惑的人们。
首先你需要知道事件分发的对象有:Activity,Window,ViewGroup和View
其次你需要知道什么是事件流:ACTION_DOWN开始,数量不定的ACTION_MOVE和ACTION_UP组成了一个完整的事件流
如果以上两点你不知道,你可以先去学习下再来看这篇文章
這篇博客不按照市面上的那些套路来讲事件分发,直接上代码吓坏了读者,也不容易让读者理解,我们都从身边的事举例子延伸到代码中
return (e = b = c = d = a = 3) == 3 ? true : false;
这条赋值语句很好懂,结果是返回true,能懂这一步你就知道了事件分发的核心思想(下面看下事件分发思想的伪代码)
return (consume=Activity.disPatchTouchEvent(ev)= Window.disPatchTouchEvent(ev)
= ViewGroup.disPatchTouchEvent(ev)= View.disPatchTouchEvent(ev))?true:false;
事件从Activity下发,一直到View结束,不断赋值,如果返回true此事件被消费了,返回false表示事件没有任何View消费
根据上面的伪代码我们可以得到如下结论:
1 每个事件分发的对象都有一个disPatchTouchEvent方法
2 如果其中一个disPatchTouchEvent返回true或者false,后面的disPatchTouchEvent就无效了(java代码里,常量不能被赋值也能和伪代码对应起来)
3 只有当disPatchTouchEvent未知的时候(super.dispatch)才可以继续往下传递形成一个完整的传递链
记住这个概念是很枯燥,那我们举个例子:
自定义ViewGroup A里面有个Button B,重写A的disPatchTouchEvent方法直接返回true,重写B的disPatchTouchEvent方法输出“B是一个快乐的Button”,当我们点击B的时候会输出什么呢?
没错,什么也不会输出,如果你很容易懂了这一点我们就继续,如果没懂,请再把上面的结论思考一遍~
当我们在网上各种百度事件分发的时候总会告诉我们有三个方法:
disPatchTouchEvent,onInterceptTouchEvent,onTouchEvent
这三个方法都是做什么的呢?和事件分发有什么关系呢?下面我用最精炼的语言总结下:
disPatchTouchEvent:消费事件(true)/不消费事件(false)/分发事件(super)
onInterceptTouchEvent: 拦截事件(true)/不拦截事件(false 默认值)
onTouchEvent : 消费(true)/不消费(false)/ 根据View属性判断是否消费(super)
接下来看任玉刚大神ViewGroup的伪代码:
pulic boolean disPatchTouchEvent(MotionEvent ev){
boolean consume=false;
if(onInterceptTouchEvent(ev)){
consume=onTouchEvent(ev);
}else{
consum=child.disPatchTouchEvent(ev)
}
return consume;
}
这段代码可以算是很精髓了,把ViewGroup到View的传递的本质描述的淋漓尽致
关于事件分发的补充:
1 只有ViewGroup有onInterceptTouchEvent方法,其他的没有
所以View的disPatchTouchEvent伪代码
pulic boolean disPatchTouchEvent(MotionEvent ev){
return onTouchEvent(ev);
2 一旦事件在传递链上被消费,后续的对象就无法接收该事件(和核心事件分发的伪代码一致)
3 如果onTouchEvent(ev)返回false,那么将传递给上一级的onTouchEvent(ev)
这个怎么理解呢?
return (consume=Activity.disPatchTouchEvent(ev)= Window.disPatchTouchEvent(ev)
= ViewGroup.disPatchTouchEvent(ev)= View.disPatchTouchEvent(ev))?true:false;
disPatchTouchEvent里面再来一段伪代码
boolean consume=false;
if(onInterceptTouchEvent(ev)){
consume=onTouchEvent(ev);
}else {
if (child.disPatchTouchEvent(ev)) {
consume = true;
}else {
onTouchEvent(ev);
}
}
return consume;