转载请注明出处:http://blog.csdn.net/chziroy/article/details/44401615
要理解Android事件分发机制,首先得了解几个概念,也算是总结,如果暂时看不懂也无妨,本文会讲解这几个问题。
1,点击屏幕,首先事件的传递从Activity的dispatchTouchEvent()方法开始。
2,关于Android事件分发机制,相关方法的方法有三个:onTouchEvent(),dispatchTouchEvent(),还有onInterceptTouchEvent(),而相关的类有Activity,View,ViewGroup。
3,时间的分发顺序为dispatchTouchEvent --- onInterceptTouchEvent --- onTouchEvent
4,Android事件分发机制,有一个向下分发过程,该过程主要调用dispatchTouchEvent,还有一个向上返回过程,主要依靠onTouchEvent方法,
5,Android事件从父视图分发到子视图,如果事件被拦截,则事件不会继续向下分发,而被当前视图消耗。此时上述的向下分发过程提前结束
6,没有被消耗的事件,从父视图逐级分发到子视图,最后又回到Activity,被Activity中的onTouchEvent()消耗。此时上述的向上返回过程提前结束
本文不会直接贴出上述相关方法和类的源代码,而会贴出其“伪代码”,方便理解。
先从事件分发的起点开始,也就是Activity的dispatchTouchEvent()方法,
public boolean dispatchTouchEvent(MotionEvent ev) {
//第一步,将事件分发
//第二步,如果事件在分发中没被消耗,则传递给Activity的onTouchEvent()方法
}
上述代码的第一步“将事件分发”,那事件会分发到哪里呢,分发到它的根布局的一个ViewGroup(其实就算你的activity的布局文件没有设置一个LinearLayout这样的根布局,系统也会默认给你加一个默认的FrameLayout)。事件分发到了ViewGroup中,就进入了ViewGroup的dispatchTouchEvent 方法,在查看该方法的源码时,Android3.0之前该方法的源码和更高版本中的源码是不同的。不过原理大致相同。以下是该方法的原理。
public boolean dispatchTouchEvent(MotionEvent ev) {
调用onInterceptTouchEvent检查是否拦截事件
if(没有拦截){
在ViewGroup中遍历查找目前是点击了哪个子视图
if(找到了){
调用该子视图的dispatchTouchEvent,递归下去
}else{
没找到,则将事件传给onTouchListener,没有Listener则传给onTouchEvent()
如果再listener或者onTouchEvent()中down事件返回了true,代表事件被消费,后续的move和up都被Listener或者onTouchEvent()处理,
如果down事件返回false,则后续的move,up事件将不会到这一层的Viewgroup,而直接在上一层视图被消费。
}
}else{
事件被拦截了,原本被点击的子视图将接收到一个ACTION_CANCEL事件,而down事件传给onTouchListener,没有Listener则传给onTouchEvent(),依然遵从上面的down和move,up事件的关系
}
}
public boolean dispatchTouchEvent(MotionEvent ev) {
//如果有listener,则把事件传递给listener
//如果没有listener,则把事件传递给onTouchEvent()
}
/**
* Implement this method to intercept all touch screen motion events. This
* allows you to watch events as they are dispatched to your children, and
* take ownership of the current gesture at any point.
*
* Using this function takes some care, as it has a fairly complicated
* interaction with {@link View#onTouchEvent(MotionEvent)
* View.onTouchEvent(MotionEvent)}, and using it requires implementing
* that method as well as this one in the correct way. Events will be
* received in the following order:
*
*
* - You will receive the down event here.
*
- The down event will be handled either by a child of this view
* group, or given to your own onTouchEvent() method to handle; this means
* you should implement onTouchEvent() to return true, so you will
* continue to see the rest of the gesture (instead of looking for
* a parent view to handle it). Also, by returning true from
* onTouchEvent(), you will not receive any following
* events in onInterceptTouchEvent() and all touch processing must
* happen in onTouchEvent() like normal.
*
- For as long as you return false from this function, each following
* event (up to and including the final up) will be delivered first here
* and then to the target's onTouchEvent().
*
- If you return true from here, you will not receive any
* following events: the target view will receive the same event but
* with the action {@link MotionEvent#ACTION_CANCEL}, and all further
* events will be delivered to your onTouchEvent() method and no longer
* appear here.
*
*
* @param ev The motion event being dispatched down the hierarchy.
* @return Return true to steal motion events from the children and have
* them dispatched to this ViewGroup through onTouchEvent().
* The current target will receive an ACTION_CANCEL event, and no further
* messages will be delivered here.
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
一言以蔽之:也就是“两个过程,两个截断”
参考过的几篇比较优秀的博客
http://codetheory.in/understanding-android-input-touch-events/
http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html#top
https://gist.github.com/Leaking/16e682b1ffac3a59c3df