一直以来都对Android 的事件分发机制有疑惑,以前只知道事件分发涉及到三个方法:dispatchTouchEvent
、onInterceptTouchEvent
、onTouchEvent
。三个方法分别为分发事件,拦截事件,处理事件,对于这些知识一知半解,并没有深入了解。当遇到滑动冲突的时候就在父View或者子View的这三个方法,return true or false
,一个个试(笨~)。上网看别人的博客,篇幅太长并且看得我一愣一愣的,我有点方。今天终于理解的比较完整了,我尝试以一种直观简单的方法描述这里面的流程。
首先也是这3个方法:
dispatchTouchEvent
,事件的分发,事件传到当前View
的话一定会调用,内部调用了onInterceptTouchEvent
和onTouchEvent
,返回结果表示是否消耗当前事件,不消耗就向上层传递。onInterceptTouchEvent
,用来判断是否拦截某个事件。onTouchEvent
,用来判断是否消耗事件。
可能看了上面3个方法有点懵逼。这里一个一个的解释。
dispatchTouchEvent
的内部实现伪代码简化之后是这样的。
@Override
public boolean dispatchTouchEvent(MotionEvent e){
boolean consume = false;
if(onInterceptTouchEvent(e)){
comsume = onTouchEvent(e);
}else{
consume = child.dispatchTouchEvent(e);
}
return consume;
}
上面的代码简单粗暴有木有?上面的代码把机制展现得淋漓尽致了。
首先调用onInterceptTouchEvent
询问是否拦截,如果拦截,就由onTouchEvent
处理,否则else
就由childView
的dispatchTouchEvent
决定。
假设我们有以下场景。有一个Activity
,然后里面嵌套了2个View
,一个是ParentView
,一个是ChildView
。那么事件的传递是怎么样的呢。
事件的传递顺序是这样的:Activity -> Window -> View
,假设我的ParentView
得到事件之后,ParentView#dispatchTouchEvent
来进行判断。然后调用了ParentView#onInterceptTouchEvent
,如果ParentView#onInterceptTouchEvent
返回true
,就会调用ParentView#onTouchEvent
。如果返回了false
就会调用ChildView#dispatchTouchEvent
,然后重复上面的过程。
这样很好理解,假设我在ParentView和ChildView的3个方法都没有做处理的话,事件的传递过程就是:
ParentView -> dispatchTouchEvent
ParentView -> onInterceptTouchEvent ->return false
ChildView -> dispatchTouchEvent
ChildView -> onInterceptTouchEvent
ChildView ->onTouchEvent
ParentView ->onTouchEvent
Activity -> onTouchEvent
还不够清楚?如下图:
只要明白了以上的原理,就能根据不同的业务需求进行拦截等相关操作了。
最后有一些小知识点需要注意的:
如果
View
设置了onTouchListener
,会先执行OnTouchListener
的onTouch
方法。如果该方法返回true
,表示消费了。不会执行onTouchEvent了
,如果onTouch
返回false
,就会继续执行onTouchEvent
如果
View
设置了OnClickListener
的话,会在onTouchEvent
中调用listener.onClick(this);
有一种场景,如果一个
ViewGroup
和它的childView
都在onTouchEvent
返回了false
,根据上面的dispathchTouchEvent
分发机制,事件会传到ViewGroup
的onTouchEvent
,如果ViewGroup
的onTouchEvent
也返回了false
的话,就会返回到Activity
,Activity
的onTouchEvent
就会被调用。
结论
一个事件的序列是从手指按下到手指抬起的整个过程,中间会有
ACTION_DOWN
,ACTION_MOVE
,ACTION_UP
的过程一个事件如果某个
View
拦截了,那么这个View
会处理整个事件序列。如果
View
决定拦截某个事件的话,就会交给onTouchEvent
处理,在一个事件序列中不会再调用onInterceptTouchEvent
。如果一个
View
的决定拦截某个事件(onInterceptTouchEvent return true)
,则在处理ACTION_DOWN
的时候,如果返回false
,表示他不处理这个事件,这时候dispatchTouchEvent
就会返回false
,那么后续事件就会交给上层View
处理,就不会再调用这个View
的onTouchEvent
了。也就是说后续的ACTION_MOVE
、ACTION_UP
都不会调用它的TouchEvent
了,都交由父级View
来处理。View
的onTouchEvent
默认返回true
。View
的longClickable
和clickable
都为false
的时候就会返回true
。childView
可以通过requestDisallowInterceptTouchEvent()
方法请求阻止或运行父View
的事件分发过程。