本文所得出的结论,均经过代码验证,请放心食用
Android View的事件分发是View知识点里面最复杂的,但不是最重要的,
我看了很多关于事件分发的文章,都是深入源码不能自拔,看得迷迷糊糊,
本文旨在屏蔽不必要的细节,简略的说说事件分发的大致思想,
可以作为事件分发的入门导读。
事件,指的是MotionEvent,
一般用户的一次触摸,能产生多个MotionEvent。
所谓View的事件分发过程,指的就是MotionEvent传递给View的过程。
说的再通俗一点就是,
MotionEvent从何而来?
MotionEvent要到哪个View去?
MotionEvent到哪个View是怎样决定的?
一.MotionEvent从何而来?
从Activity来,
所有事件都是从Activity开始传递的,
具体传递方向为:
Activity->Window->ViewGroup->View
二.MotionEvent要到哪个View去?
到消耗它的View去。
所有的View都有View.onTouchEvent(MotionEvent event)方法,
其返回结果表示是否消耗了事件,
当事件传递到这个方法,且这个方法返回了true,
那这个事件会被消耗,不再传递。
三.MotionEvent到哪个View是怎样决定的?
为屏蔽不必要的细节,在回答这个问题前,先说一个结论:
View的事件传递是U形的,
由父View传给子View,
子View不处理*,
就会传回给父View。
*:不处理指的是:
不消耗类型为MotionEvent.ACTION_DOWN的事件,
即在View.onTouchEvent(MotionEvent event)中,
对于类型为MotionEvent.ACTION_DOWN的事件返回false,
当然,只要不消耗类型为MotionEvent.ACTION_DOWN的事件,
后续事件也不会传给这个View了

MotionEvent传到哪个View由这四个方法来决定:
1).View.onTouchEvent(MotionEvent event)
上面第二个问题我们已经见过它,这个方法用于消耗事件,
返回结果表示是否消耗了事件。
一般情况下给View设置了点击回调,这个方法就会返回true。
2).ViewGroup.onInterceptTouchEvent(MotionEvent ev)
ViewGroup专有方法,返回结果表示是否拦截点击事件
3).ViewGroup.dispatchTouchEvent(MotionEvent ev)
4).View.dispatchTouchEvent(MotionEvent ev)
方法3和方法4是同一个方法,
只不过一个在ViewGroup,一个在View,
ViewGroup继承自View,重写了方法4
方法4总是会默认回调方法1,
至于方法3
重点来了!!!
方法3是事件分发逻辑的核心,
其内容阐述了四个方法间的关系,
可用以下伪代码表示:
public boolean dispatchTouchEvent(MotionEvent ev){//方法3
boolean consume = false;//表示是否消耗事件
if( !onInterceptTouchEvent(ev) ){//方法2
......
/*
上面省略号代表的代码大致写的是,
寻找一个能处理这个事件View,并赋值给child
如果没有这样的View,child为null
*/
if (child != null) {
consume = child.dispatchTouchEvent(ev)//方法3
}
}
if(child == null){
/*
super.dispatchTouchEvent(event)
会默认回调View.onTouchEvent(MotionEvent event),即方法1
*/
consume = super.dispatchTouchEvent(event)//方法4
}
retutn consume;
}
代码逻辑非常简单:
先判断自身需不需要拦截事件,
由方法2决定,
方法2默认返回false,
我们可以通过重写方法2来控制是否拦截事件。
如果拦截,那么会跳过寻找child的代码,
child为null,会执行super.dispatchTouchEvent(event),
而super.dispatchTouchEvent(event),就是方法4,
方法4总是会默认调用方法1,
即自身处理事件,事件会传到自身的onTouchEvent方法中。
如果不拦截,就寻找有没有能处理事件的子View,
如果有,
就会调用子View的dispatchTouchEvent(event)方法,
即子View处理事件,事件会传到子View的onTouchEvent方法中。
至于什么样的子View能处理事件,其实上面结论的括号内容中已经提到:
消耗类型为MotionEvent.ACTION_DOWN的事件,
即为处理,后续事件会继续传给这个View,
不消耗类型为MotionEvent.ACTION_DOWN的事件,
即为不处理,后续事件不会传给这个View。
可以看到事件在向下传递的过程中,
是通过层层调用dispatchTouchEvent(ev)向下传递的。
还值得一提的是,
方法2其默认返回值为false,
也就是说,ViewGroup总是默认把事件传给子View。
事件分发的大致流程就是这样,
父传子,子不处理就传回给父,至于子处不处理,
则看子View消不消耗类型为MotionEvent.ACTION_DOWN的事件,
而父传子的过程中,父View不想往下传了,想自己处理事件
可以通过重写onInterceptTouchEvent方法返回true实现。
end
网友评论