这片文章讲解的事件传递的起源从dispatchTouchEvent(event)开始,根据事件的处理流程逐渐展开,直至事件被可预料的处理掉结束。
先贴一张个人总结的事件传递的流程图,如果可以将这张图清楚的理解,下面的文章就可以不用看了,因为这篇文章的主要内容也就是围绕这幅图展开。
一、ViewGroup中的事件处理
当事件传递到dispatchTouchEvent方法中,由于ViewGroup是View的子类,所以如果当前接收到事件的是ViewGroup将首先触发该类的dispatchTouchEvent方法(不过android界面最外层肯定是个ViewGroup所以肯定会先触发这个事件)。
该方法会先判断该事件是否可被中断(默认为是),然后便触发ViewGroup才有的onInterceptTouchEvent方法,该方法用于ViewGroup截取事件处理,假如该方法返回true则代表已经将事件截取。
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
onInterceptTouchEvent方法。而在ViewGroup的源码中并没有做截取动作,直接返回了false。
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
在确定事件没有被截取之后,ViewGroup中会判断自己是否有子视图符合接收该事件的条件,如果有的话,则直接将事件分发给该子视图,并返回true代表在这层事件已经被分发出去。否则的话则将该视图视为普通View继续处理事件(详细参见后面的View处理事件部分)。
if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
二、View中的事件处理
不管上面的那种情况最终都会通过dispatchTransformedTouchEvent方法最后调用到View中的dispatchTouchEvent方法。该方法首先会调用到的是mOnTouchListener.onTouch方法,其中mOnTouchListener是我们在使用view的setOnTouchListener方法时注册进去的监听。如果我们在注册进去监听的onTouch方法中返回true则代表该事件已经被处理,事件将不会在继续传递。
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
而如果没有设置监听或返回为false的话,该事件将会被传递到onTouchEvent方法。在View类的onTouchEvent方法中,由于是最简单的视图,只处理了和click相关的事件。从注释可以看出包括click和longClick事件。
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
在performClick中则调用到我们为view设置的clicklistener监听。
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
return true;
}
在这里我没有看到关于longClick事件的处理,暂时还没有找到,下次找到了再继续更新。
三、总结
- 首先接收到事件的视图是最外层的视图,然后再往子视图上传递;
- 事件的传递是一个递归过程;
- 在上述每个环节都可以通过返回true的方法,结束事件的传递;
- 自定义视图的事件传递过程决定于其对应继承的方法,但应该遵守上述的规则。