从伪代码理解View事件分发过程
事件从起源
从手指从屏幕按下的瞬间,触摸事件经过一系列处理会来到Activity
的dispatchTouchEvent
中。
Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//getWindow().superDispatchTouchEvent(ev) 返回true代表消费了事件
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//否则调用Activity的onTouchEvent
return onTouchEvent(ev);
}
getWindow()
实际返回的是PhoneWindow
PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
getWindow().superDispatchTouchEvent(ev)
实际会调用到mDecor.superDispatchTouchEvent(event)
DecorView.java
public class DecorView extends FrameLayout
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
}
跟踪DecorView
会发现DecorView
继承自FrameLayout
,因为FrameLayout
没有重写dispatchTouchEvent
方法,所以事件从Activity
一路下来,最终事件的入口是ViewGroup
的dispatchTouchEvent
。
开发中,事件一般通过层层ViewGroup
传递到View
中,进行消费。一般View
做真正的事件消费。
View的事件分发
View.java--伪代码
/**
* view接收事件的入口,事件由ViewGroup分发过来
*/
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
//如果设置了OnTouchListener,并且mOnTouchListener.onTouch返回了True,
//设置Result为True,那么代表事件到这已经消费完成了。
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
result = true;
}
//没有设置OnTouchListener,或者mOnTouchListener.onTouch返回了false时,result为false
//此时会回调View.onTouchEvent方法
if(!result&& onTouchEvent(event)){
result = true;
}
return result;
}
public boolean onTouchEvent(MotionEvent event) {
//如果设置了onClickListener,那么返回True代表事件到这已经消费完成了。
if (onClickListener != null) {
onClickListener.onClick(this);
return true;
}
return false;
}
dispatchTouchEvent
是传入事件的入口,如果设置了mOnTouchListener
,并且返回了true
,那么dispatchTouchEvent
就会返回true
,代表事件被当前View
消费了。如果没有设置,那么就会回调onTouchEvent
方法,如果设置了onClickListener
,那么onTouchEvent
返回true
,同理dispatchTouchEvent
就会返回true
,代表事件被当前View
消费了.
从上面可以看出OnTouchListener
先于onTouchEvent
执行,onTouchEvent
先于onClickListener
执行。
ViewGroup的事件分发
ViewGroup.java--伪代码
/**
* onInterceptTouchEvent 拦截事件
* @return true 代表拦截当前事件,那么事件就不会分发给ViewGroup的child View ,会调用自身的 super.dispatchTouchEvent(event)
* false 代表不拦截当前事件,不拦截事件,那么在dispatchTouchEvent会遍历child View,寻找能消费事件的child View
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
/**
* @param event 事件
* @param child 如果child 不为null,那么事件分发给它,否则,调用调用自身的 super.dispatchTouchEvent(event)
* @return 是否消费了该事件
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, View child) {
boolean handled = false;
if (child != null) {
handled = child.dispatchTouchEvent(event);
} else {
handled = super.dispatchTouchEvent(event);
}
return handled;
}
public boolean dispatchTouchEvent(MotionEvent event) {
boolean handled = false;
//是否拦截当前事件
boolean intercepted = onInterceptTouchEvent(event);
//触碰的对象
TouchTarget newTouchTarget = null;
int actionMasked = event.getActionMasked();
if (actionMasked != MotionEvent.ACTION_CANCEL && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN) {
//ViewGroup child View 数组
final View[] children = mChildren;
//倒序遍历,最后的通常是需要处理事件的
for (int i = children.length - 1; i >= 0; i--) {
View child = mChildren[i];
//isContainer 方法判断事件是否落在View中
if (!child.isContainer(event.getX(), event.getY())) {
continue;
}
//找到可以接收事件的View,把事件分发给他,
//如果dispatchTransformedTouchEvent返回了True代表消费了事件
if (dispatchTransformedTouchEvent(event, child)) {
handled = true;
//通过child包装成TouchTarget对象
newTouchTarget = addTouchTarget(child);
break;
}
}
}
}
//如果TouchTarget为null,那么事件就发就自己处理
//mFirstTouchTarget == null 在onInterceptTouchEvent返回true时,或没有找到可以消费的child View时成立
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(event, null);
}
return handled;
}
dispatchTouchEvent
是事件接收的入口,如果拦截事件,那么就调用super.dispatchTouchEvent(event)
,我们知道ViewGroup
是继承View
的,那么调用super.dispatchTouchEvent(event)
等于调用View
的dispatchTouchEvent
。
View.java
/**
* view接收事件的入口,事件由ViewGroup分发过来
*/
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
//如果设置了OnTouchListener,并且mOnTouchListener.onTouch返回了True,
//设置Result为True,那么代表事件到这已经消费完成了。
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
result = true;
}
//没有设置OnTouchListener,或者mOnTouchListener.onTouch返回了false时,result为false
//此时会回调View.onTouchEvent方法
if(!result&& onTouchEvent(event)){
result = true;
}
return result;
}
如果不拦截,那么就会遍历当前ViewGroup
的child view
,找能消费事件的View
,如果找到,调用dispatchTransformedTouchEvent(event, child)
,这里的child
可以是ViewGroup
或者是View
,最后根据dispatchTransformedTouchEvent
返回值判断是否消费了事件,如果返回false
后,那么调用ViewGroup
的super.dispatchTouchEvent(event)