public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onFilterTouchEventForSecurity(ev)) {
return false;
}
final int action = ev.getAction();
//获取事件的坐标
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
//disallowIntercept 默认是false,
//可以通过requestDisallowItercepctTouchEvent来设置参数
//被设置成true后,ViewGroup无法拦截除ACTION_DOWN以外的事件(只能拦截Down)
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//这里是ACTION_DOWN的处理逻辑
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
// this is weird, we got a pen down, but we thought it was
// already down!
//We should probably send an ACTION_UP to the current target
mMotionTarget = null;
}
// If we're disallowing intercept or if we're allowing and we didn't
// intercept
//默认情况下disallowIntercept为false,表示允许拦截,
//默认情况ViewGroup的onInterceptTouchEvent返回false
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// reset this event's action (just to protect ourselves)
ev.setAction(MotionEvent.ACTION_DOWN);
// We know we want to dispatch the event down, find a child
// who can handle it, start with the front-most child.
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {//遍历子View
final View child = children[i];
//判断子元素是否可以接收到事件,两条件决定
//条件一:子View是VISIBLE或者在播动画
//条件二:点击坐标落在子View区域内(体现在内嵌的if)
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
//判断是否点击坐标落在子控件区域内
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
//将事件派发给子View,返回true表示子View处理该事件,
if (child.dispatchTouchEvent(ev)) {
// Event handled, we have a target now.
mMotionTarget = child;//将处理事件的目标View保存在变量
return true;//返回true,表示消耗事件
}
// The event didn't get handled, try the next view.
// Don't reset the event's location, it's not
// necessary here.
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
//重置mGroupFlags,
//使得在下一个事件ACTION_DOWN来临时disallowIntercept为false
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// The event wasn't an ACTION_DOWN, dispatch it to our target if
// we have one.
final View target = mMotionTarget;
if (target == null) {//没有找到可以处理事件的子View
// We don't have a target, this means we're handling the
// event as a regular view.
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
//子控件不处理,所以此处判断一下自己是否处理
//此时ViewGroup调用的是父类View的dispatchTouchEvent
return super.dispatchTouchEvent(ev);
}
// if have a target, see if we're allowed to and want to intercept its
// events
//允许并且想要拦截事件
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);//设置ACTION_CANCEL
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {//告知目标子控件事件被拦截
// target didn't handle ACTION_CANCEL. not much we can do
// but they should have.
}
// clear the target
mMotionTarget = null;//重置为null,使得下个事件来临时target=null
// Don't dispatch this event to our own view, because we already
// saw it when intercepting; we just want to give the following
// event to the normal onTouchEvent().
return true;//返回true,表示事件被消耗
}
if (isUpOrCancel) {
mMotionTarget = null;
}
// finally offset the event to the target's coordinate system and
// dispatch the event.
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
//将事件分发给目标子控件
return target.dispatchTouchEvent(ev);
}
ACTION_DOWN事件用流程图表示如下:
ACTION_MOVE事件被分发到ViewGroup的时候,从第81行可以看到首先会判断target是否等于null,若等于null,表示子控件均不能消耗事件,则调用super.dispatchTouchEvent即View的dispatchTouchEvent来处理事件。若不等于空,此时会来到第97行,此时通过条件(!disallowIntercept&&onInterceptTouchEvent(ev))判断是否拦截,若条件不成立表示不拦截则执行第131行代码,调用target.dispatchTouchEvent将事件分发给目标子控件处理,如果拦截则首先生成ACTION_CANCEL事件(见第101行)并分发给目标子控件target(见第103行),告知事件已被拦截,之后执行第108行将mMotionTarget重置为null,目的是让接下来的ACTION_UP事件直接能给ViewGroup自己处理,最后在第112行返回true表示事件被消耗。
ACTION_MOVE事件用流程图表示如下:
ACTION_UP分发到ViewGroup的时候,首先会通过执行mGrouFlags & = ~FALG_DISALLOW_INTERCEPT使得下一个ACTION_DOWN事件来临时disallowIntecept重置为默认的false,之后的处理逻辑和ACTION_MOVE基本一致,这里不再重复
ACTION_UP事件用流程图表示如下: