事件分发在Android开发中无处不在,只要是控件都离不开事件的分发,事件就是控件的灵魂,本文剖析下的ACTIONDOWN事件分发原理;
从用户点击屏幕那一刻开始,在java层首先接触到事件的是Activity我们看下Activity中接收事件并分发的代码
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
第一个判断条件中的onUserInteraction(),是一个空实现,用来Activity接收的事件之后重写的方法,我们重点看第二个判断条件getWindow.superDispatchTouchEvent(),,我们都是知道window有一个唯一的子类phoneWindow,所以此方法实际上调用了phoneWindow的superDispatchKeyEvent(ev)方法如下图,
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
我们可以看到此方法又调用了顶层的DecorView的superDispatchTouchEvent()方法,我们继续往下跟踪:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
我们都知道DecorView是继承自FrameLayout,所以 super.dispatchTouchEvent(event),实际上是调用了ViewGroup的 dispatchTouchEvent(event)方法;
到这里我们可以做一个小结:用户点击屏幕以后,事件的传递顺序是:Activity---PhoneWindow---DecorView---ViewGroup---View
接下来我们先来看ViewGroup的dispatchTouchEvent方法,由于原码逻辑比较多,所以这里我进行了适当的抽取,只选择了比较核心的DOWN事件的传递
public boolean disPatchTouchEvent(MotionEvent motionEvent) {
TouchTarget newTouchTarget = null;
boolean handled = false;
boolean intercepted = onInterceptTouchEvent(motionEvent);
int actionMasked = motionEvent.getActionMasked();
if (actionMasked != MotionEvent.ACTION_CANAEL && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN) {
final View[] children = mChildren;
for (int i = children.length - 1; i >= 0; i--) {
View child = children[i];
if (!child.isContainer(motionEvent.getX(), motionEvent.getY())) {
continue;
}
if (dispatchTransformedTouchEvent(motionEvent, child)) {
handled = true;
newTouchTarget = addTouch(child);
break;
}
if (mFirstTouchTarget == null) {
}
}
}
}
return handled;
}
private TouchTarget mFirstTouchTarget;
private TouchTarget addTouch(View child) {
TouchTarget touchTarget = TouchTarget.obtain(child);
touchTarget.next = mFirstTouchTarget;
mFirstTouchTarget = touchTarget;
return touchTarget;
}
public boolean dispatchTransformedTouchEvent(MotionEvent motionEvent, View
childView) {
boolean isDispatch;
if (childView != null) {
isDispatch = childView.disPatchTouchEvent(motionEvent);
} else {
isDispatch = super.disPatchTouchEvent(motionEvent);
}
return isDispatch;
}
public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
return false;
}
我们可以看到dispatchTouchEvent里面,首先是根据onInterceptTouchEvent()方法来判是否要阻断点击事件,这里我默认返回是false也就是不阻断,然后开始循环遍历Group中的子控件,我们可以看到isContainer()方法,此方法实际上就是用来判断点击事件的范围是否在子控件的边界范围之内,如果在我们就确定此控件可以接受事件,如果不在则跳过,继续遍历下一个;当找到第一个可以接受事件的子View后,就开始执行dispatchTransformedTouchEvent方法,可以看到我们把接受事件的child对象传入到了此方法,然后调用childView的disPatchTouchEvent方法,如果childView是ViewGroup那么会继续调用上面disPatchTouchEvent方法继续遍历这个childView,相当于递归自己调用自己;如果childView是View,那么直接调用View的disPatchTouchEvent
public boolean disPatchTouchEvent(MotionEvent motionEvent) {
boolean result = false;
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, motionEvent)) {
result = true;
}
if (!result && onTouchEvent(motionEvent)) {
result = true;
}
return result;
}
我们看到当事件来到view的dispatchTouchEvent以后,就会判断用户是否设置了onTouchListener,也就是为什么我们会说触摸监听的优先级会高于触摸事件的优先级, 如果触摸监听的回调方法onTouch返回了ture,那么result就不会就下面的onTouchEvent方法,如果返回用户没有返回false或者说onTouch方法返回false,那么就会继续调用onTouchEvent方法,
public boolean onTouchEvent(MotionEvent motionEvent) {
if (onClickListener != null) {
onClickListener.onClick(this);
return true;
}
return false;
}
如果用户设置了点击事件,那么就代表用户消费了此事件,那么时间传递就结束,如果onTouchEvent返回false,相当于上面的childView的dispatchTouchEvent方法返回了false,相当于ViewGroup的dispatchTransformedTouchEvent方法返回了false那么就会走到
if (mFirstTouchTarget == null) { //这里面就又会调用 dispatchTransformedTouchEvent(MotionEvent motionEvent, null) }
public boolean dispatchTransformedTouchEvent(MotionEvent motionEvent, View childView) { boolean isDispatch; if (childView != null) { // isDispatch = childView.disPatchTouchEvent(motionEvent); } else { //相当于子View没有消费时间,那么就会调用父类的disPatchTouchEvent, //相当于又会调用View的disPatchTouchEvent方法,注意此时的view代表的ViewGroup //表示子View没有消费事件,所以事件就传给ViewGroup消费了 isDispatch = super.disPatchTouchEvent(motionEvent); } return isDispatch; }
到这里相信大家应该能够明白,事件分发的U型图的含义了,以上就是down时间的分发原理;