package android.view;
public abstract class ViewGroup extends View implements ViewParent, ViewManager
//ViewGroup分发事件方法
//该方法返回false表示ViewGroup自身和子View都没有人消费事件,返回true表示
//有人消费了,具体谁消费的,保存在mFirstTouchTarget中,这样后续就不用再一个个遍历查找了
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
//如果当前是down事件,重置参数
cancelAndClearTouchTargets(ev);
resetTouchState();
}
final boolean intercepted;
//如果是down事件 或者 已经找到能消费事件的子view了,进入下面if
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//如果是down事件,那么肯定要判断是否拦截事件,如果是move事件并且已经找到可以消费事件的子view,那么每次调用move方法的时候,也是要判断是否拦截的,并不是down事件不拦截,然后move、up也不判断拦截。另外,FLAG_DISALLOW_INTERCEPT这个变量是子view 申请父控件不要拦截事件的变量,具体在子view中获取父view调用requestDisallowInterceptTouchEvent()来实现。
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//如果子view没有要求父view拦截事件
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
//子view要求父view拦截事件
intercepted = false;
}
} else {
//如果不是down事件(move或up)并且 没有找到能消费事件的子view,就直接拦截了,也就是不要把后续事件再交给子view了,
//全由父view自己处理,也就是当前viewGroup。
intercepted = true;
}
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
//如果需要,更新指针向下的触摸目标列表
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
//这个单链表对象记录能消费事件的子view
TouchTarget newTouchTarget = null;
//是否已经发送事件到目标子view
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
//如果没有拦截事件,就会进这个方法。
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); //总是为0,代表从上往下
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
//down事件,多手指事件都会进来这个方法,move,up不进来。
//清除此指针id的早期能够消费事件的view,以防它们已经不同步了
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
//如果暂未找到能够消费事件的子view并且子view不为0,那么遍历查找能够消费事件的子view.
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
//buildOrderedChildList()表示按照z轴从上往下排列子view,用于寻找可消费事件子view的时候从最上面找。
final ArrayList<View> preorderedList = buildOrderedChildList();
//判断是否要重新排序
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
......
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
//如果子view不能收到点击事件或者子view不在点击的范围之内,跳过该view
continue;
}
//第一次down事件进来从链表中是拿不到对象的,所以newTouchTarget必然返回null
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
//如果从链表中拿到了能够消费事件的子view,则直接跳出循环,防止每次都重新遍历一遍。
break;
}
//分发事件给子view去处理,返回true则有子view消费,返回false没有子view消费
//返回true创建newTouchTarget并赋值给mFirstTouchTarget变量
//一直递归找到子view中能消费的就break退出循环。
//这里走的是子view的handled = child.dispatchTouchEvent(event);方法
//递归过程:子view是viewgroup则继续分发事件,子view是view则开始处理事件,包括执行onTouch方法等。
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//最终经过漫漫递归终于沿着一条路径找到了能够消费事件的view链条,逐级返回到这里为true。
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//newTouchTarget这里赋值,不再为null,并且将newTouchTarget赋值为mFirstTouchTarget
//按照从最终消费的子view一层层往上回调的逻辑,这里的链表应该是永远往头部插入节点,最后形成的结构是从顶级父view到最终的子view,从上到下的一个单链接结构,头部节点为顶级viewGroup下一级能够消费的子view,尾部就是最后处理事件的子view。
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//标记已经将事件分发给了子view并且子view能够消费(也就是说alreadyDispatchedToNewTouchTarget标记了本view肯定不是消费事件的view,而是它的下级们才有可能消费事件)。
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
} // for循环结束
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
//move,up事件到这里开始执行啦
if (mFirstTouchTarget == null) {
//无接触目标,因此将此视为普通视图
//遍历完都没有找到能够消费事件的子view,则super.dispatchTouchEvent()自己处理事件。
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//发送到触摸目标,如果我们已经发送到新的触摸目标,则不包括它。必要时取消触摸目标。
//遍历完找到了能够消费事件的子view,这里处理move事件,不用再遍历了,直接找到mFirstTouchTarget。
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//开始遍历从顶级到最终消费事件的子view的所有路径,如果分发过事件的,直接handled=true,如果没有分发过事件的,就将事件分发给它处理。
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
//已经发送到新的目标的view肯定不是处理事件的view,直接返回true就可以了
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
//分发事件给找到的子view
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
//事件返回true
handled = true;
}
if (cancelChild) {
//子view取消事件,然后跳过该子view?
if (predecessor == null) {
//mFirstTouchTarget
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
//
predecessor = target;
target = next;
}
}
//取消或则up事件走这里
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
//更新指针
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
//取消的触摸目标列表(如果需要)
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
return handled;
}
//child参数为null,父view自己处理事件,child参数不为null,执行事件的分发。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
//取消事件走这里流程
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
//父view分发过来的事件cancel都为false,走下面流程,也就是没有取消事件.
// 计算要传递的指针数.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
if (newPointerIdBits == 0) {
return false;
}
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
if (child == null) {
//当父view没有找到能够消费事件的子view后调用dispatchTransformedTouchEvent(),并在child参数中传null,这样就让自己来处理事件了。
handled = super.dispatchTouchEvent(transformedEvent);
} else {
handled = child.dispatchTouchEvent(transformedEvent);
}
return handled;
}
//调用super.dispatchTouchEvent就会来这里
//这里处理view的事件
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//1、判断view是否设置了OnTouchListener事件监听
//2、以及view是否可用
//3、调用onTouch方法,如果返回值为true,则整个方法返回true,表示消费事件,
//后面流程都不走了
result = true;
}
if (!result && onTouchEvent(event)) {
//onTouch方法返回false才进来这里,调用onTouchEvent方法
result = true;
}
}
return result;
}
//View消费事件的方法
//返回true表示消费事件,返回false表示不消费
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (prepressed) {
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
//使用Runnable并发布此消息,而不是直接调用performClick。
//这允许在单击操作开始之前更新视图的其他可视状态
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
//没有放进消息队列就直接调用
performClick();
}
}
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
boolean isInScrollingContainer = isInScrollingContainer();
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
setPressed(true, x, y);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
......
break;
case MotionEvent.ACTION_MOVE:
......
break;
}
return true;
}
return false;
}