阅读源码的初衷
因为决定学习design控件的时候在用到AppBarLayout在看到NestedScrollParent,觉得AppBarLayout东西很神奇,它可以在关联的滑动控件滑动的时候自己根据设置的模式来自己滑动。感谢互联网上的大神,看他们的文章在学习怎么使用这个控件的时候,他们把能够关联滑动的原理也带着讲了下。其实AppBarLayout这个控件实现的是NestedScroll这个接口,这个接口有2个一个是NestedScrollParent,一个是NestedScrollChild。当然这2个接口怎么用,如何用目前不是这个文章的关注点。Nested这个单词是嵌套的意思,那么NestedScroll就是嵌套滑动的接口。这个接口很有意思,如果实现了这2个接口。AppBarLayout这个关联的滑动对象,暂时用观察者模式来说明便于理解(当然这里是不是用的观察者模式,我还不确定,得研究源代码才能知道用的是哪种方式)。假设关联的滑动对象为被观察者,Appbarlayout为观察者。那么被观察者滑动的时候,会通知观察者我滑动了,这个时候Appbarlayout会根据xml或者java代码里面设置的模式来滚动,这里有意思的地方在于,被观察者滚动的时候,滚动的距离会告诉观察者,观察者做出相应的动作,然后将没滚动完的距离交给被观察者,剩余的未滚动完的距离被观察者你可以去消耗这个滚动距离了(当然这里是用作一种的一种模式来做说明)。因为没去看NestedScroll相关的源代码,在网上找了下这个接口的作用,大家给的都很模糊,说是在onTouchEvnet函数中处理的相关接口,怎么处理的不知道。所以我在想如果不用nestedscroll这个接口,我们要自己实现相关的效果,这里肯定事件的处理,也即是事件分发。所以促使我去阅读android事件分发的源代码。
事件分发基础
我们都知道事件分发最先触发顶层viewgroup的dispatchToutchEvnet的这个方法,在这个方法的内部去遍历他的子控件来一步一步的进行分发,当然这里如果所有的view都不拦截的话,那么这个事件的分发顺序就是由外至内来分发,如果底层的控件不处理那么一步一步的由内至外的来通知处理。那么我们一般做事件分发的时候一般是通过在事件拦截中去处理相关的业务,然后决定是否将事件分发下去,不分发就由父控件来消耗事件,如果分发则由子控件来消耗事件,那么NestedScroll这个接口为什么会在onTouchEvent中去处理,要知道,如果onTouchEvent当中返回了false,那么该view的onTouchEvent不在接收后续的事件。正是因为带着这样的疑惑,我决定先从事件分发的源码开始看起,在去看NestedScroll的接口实现。在事件分发当中,无疑dispatchTouchEvent这个方法会先触发,那么我们从这个方法跟进去看看系统到底是怎么处理的。下面是viewGroup里面的源代码
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
//取消或者清除触摸目标
cancelAndClearTouchTargets(ev);
//重置所有触摸状态,为新的循环做准备
resetTouchState();
}
// Check for interception.
//分析1开始
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
//分析1结束
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//分析2开始
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
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;
}
}
}
//分析2结束
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
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);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
现在我们将分析1和分析2中的关键代码段提取,并精简,其中各种设置状态,常量,标志等等的代码去掉,关注核心代码,则分析1的片段代码为
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
这里声明了一个intercepted变量,这个变量的作用是接受该viewgroup的拦截事件的返回值,首先的一个if判断是判断事件是否是down事件,disallowIntercepter这个变量是requestDisallowInterceptTouchEvent();通过这个方法来设置改变这个值的,allow是允许,intercepter是拦截,allowintercepter是允许拦截,前面加个dis就是不允许拦截的意思,一般如果没有调用requestDisallowIntercepterTouchEvent这个方法那么disallowIntercepter这个值恒等于false,这个时候则会进入第二个if判断,通过interceptered=onIntercepteTouchEvent(ev);这句代码就知道interceptered接收的是拦截事件的返回值。我们进入这个代码进去看看他里面的业务。
//这是android 4.4的源代码
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
//这是android 28的源代码
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
这就是我为什么不从android 9的源代码去看事件分发,因为里面有些新的东西比android 4.4复杂多了。闲话不说,先说说在你不重写onInterceptTouchEvent方法的时候,该方法的返回值恒等于false。那么通过以上可以得知在不调用requestDisallowIntercepterTouchEvent去设置disallowIntercepter值,并且处于按下操作时,那么一定会进入分析1中的第二个if条件判断,这里就会进入到事件拦截方法当中去,然后重新将ev事件源对象重置他的action。那么第二个else就不会进入了intercepted值为false。那么这里的这个mFirstTouchTarget是个什么东西呢?我也不知道,我们保持着这个疑问去后面找找,看能不能找到这个东西到底是什么,因为我们知道一个事件序列是由一个down 多个move和一个up组成,当ev的action为down的时候,可以不用管mFirstTouchTarget是个什么东西都会进入到他的if判断里去,但是当action不为down的时候,那么mFirstTouchTarget决定了分发的流程问题。所以我们带着疑问去后面代码里面找找看有没有哪里能看到mFirstTouchTarget东西的赋值。带着这个疑问我们在进入分析2的代码块
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
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;
}
}
}
canceled这个值在没有对view的mPrivateFlags值进行改变的时候该值是一个固定值,那么在dispatchTouchEvent代码中并没有相关的代码去设置view的mPrivateFlags值,那么我们可以认为canceled这个值是一个固定的值(这个值是false)。那么这时第一个if条件判断则成立那么则进入第二个if判断,第二个判断力的split值是个boolean变量他的最终值取决于ViewGroup的mGroupFlags值,这种标志值等等的不带入分析,那么我们可以认为这个值我们没有去做修改,是系统的一个默认值。那么split这个值就等于true。ACTION_POINTER_DOWN和ACTION_HOVER_MOVE不在我们研究范围,一个是多点触摸,一个是鼠标的移动事件,我们只是分析简单的single finger的事件。第二个条件判断的条件即是为如果是按下事件则进入if条件内部,否则不进入。那么这里的第三个if判断的第一个条件在刚进入的时候newTouchTarget 是为空的,具体可以看dispatchTouchEvent方法中,声明了该变量比赋值为null,并且在这个if判断之前并没有对newTouchTarget 进行赋值,那么可以认为这个条件成立mChildrenCount则是这个viewgroup的子控件数量是否为0。那么在viewgroup有子控件且不为0的情况下,则if条件判断成立进入if内部,那么这个if内部则是循环去遍历viewgroup内部的子控件,拿到子控件之后进入后一个if判断,那么这里的判断第一个条件是这个可见的子view是否能接收事件,后面的一个判断条件是down事件的X.Y点坐标是否在可见的控件内部,这里我们认为一个可见的控件一定是可以接收事件的,那么前面一个条件为假,我们点击的地方在可见控件的内部,则后面一个条件为假。那么就会去继续循环遍历。知道将所有的子view遍历完,找到既可以接收事件有在该点击事件内部的view的对象,将发生down事件时的其他子view对象排除掉了。接着我们来看看getTouchTarget(view)这个方法。这个方法的内部实现是
private TouchTarget getTouchTarget(View child) {
for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
if (target.child == child) {
return target;
}
}
return null;
}
可以看到这里的TouchTarget 是个链表结构,在这里我们找到我们感兴趣的东西mFirstTouchTarget,虽然这个东西是个什么目前还不清楚,但是知道了mFirstTouchTarget值目前是个null值,那么这个循环体在一开始进入不到if判断里去直接返回null值,注意这里的目前表示当你触发down事件时,不是代表永远返回null。那么我们得到newTouchTarget 的值为null,那么如下代码则不会执行,
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;
}
我们现在在看看
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;
}
现在则会进入这个if判断,那么我们来看看这个这个dispatchTransformedTouchEvnet方法是用来干什么的
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
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;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
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);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return 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;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
对于这段代码,很好理解,cancel是方法传过来的false值,那么只要传过来的event事件不为cancel,那么if条件是一定不成立的,里面的代码块就略过,下面的oldPointerIdBits 和newPointerIdBits这2个对象应该是1,因为我们只研究单点触控。那么下面的if条件也不会进入,就略过了
那么我们进入下一个条件判断的代码块
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);
}
刚刚我们分析了在单点触控的条件下这2个bit都为1,那么第一个if条件满足进入到第二个if条件,这里的child不为null那么会到transformedEvent = MotionEvent.obtain(event);这个语句,相当于是把传进来的evnet copy了一份赋值给transformedEvent执行到这里我们可以看到实际上就是把down事件copy一个赋值给声明的event变量。那么在往下看
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
这里第一个if条件肯定不符合,那么我们看看else里的内容,前面的变量是计算那么进入else,这里else其他部分不管他看handled = child.dispatchTouchEvent(transformedEvent);这句,就将copy的event事件交给子view去分发,如果子view是viewgroup那么有重新将这个流程走一遍,如果不是则进入view的dispathTouchEvent方法中处理。那么我们在回到viewgroup的dispatchTouchEvent方法中去看看后面的执行过程
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;
}
这是我们上面分析的调用了dispatchTransformedTouchEvent方法,子view的分发是在这里发生的,那么我们在做事件分发的时候一般很少会去重写dispatchTouchEvent来改变他的分发方式,这里我们认为他一直返回true这里我们可以看到newTouchTarget这个链表结构的对象在这里被赋值了,我们进入这个方法去看看里面的源码
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
这里我们可以看到这个obtain方法是组装一个target,并将组装的target返回,那么这里的链表的指针域指向一个null,并且将组装的这个target赋值给了mFirstTouchTarget ,那么我们带着的疑问解开了,即是mFirstTouchTarget 这个东西是当你down事件发生的时候,保存的view的对象,以及一些其他的bit类的数据,这里相当于往这个链表结构里面添加的一个节点。那么至此我们获得了mFirstTouchTarget,newTouchTarget 对象,到这里我们整个分析2全部分析完了。接着看看下面的代码片段。
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
这里拿到了2个对象都不为null那么这里的整个if条件都不会进入。至此整个分析2开始的if条件判断语句里的所有源代码分析完毕。接下来,我们看看最后一部分源代码做了什么。
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
在这里按照正常情况下down事件发生, 我们可以肯定alreadyDispatchedToNewTouchTarget 这里为true,target == newTouchTarget为真,因为newTouchTarget这个对象在组装的时候,mFirstTouchTarget和newTouchTarget指针指向的是一个地址。那么handle为true,那么else不进入,predecessor就是你遍历后找到的最底层的那个view所封装成的target对象,也就是mFirstTouchTarget对象,因为next对象指向的是target的指针域,而target指针域为null。那么最后target=next=null。(对于数据结构,我不是太懂,只是按照自己的理解说的,因为不是计算机科班专业出生的,所以这里的数据结构的解释可能不正确,分析源代码到这里发现有必要去学习下数据结构这个东西,要不然源码会让你一脸的懵逼)。
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);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
这是整个dispatchTouchEvent最后一段代码了,这里canceled为false,另外2个条件一个是是否抬起,一个是鼠标在上面滑动,如果有一个为真,那么久重置touch状态。else if里面是判断多点触控的有一个触控点抬起可以不管这个,最后一个条件handled的值我们刚得到的为true,那么handled去反,前一个条件始终不成立,那么这里的if条件进不去,所以返回true。到这里我们android4.4的ViewGroup的dispatchTouchEvent分析完毕。下篇我会在继续看看View的dispatchTouchEvent的源代码。希望能从源码的角度去看看,为什么OnTouchEvent这个方法当返回false的时候后续的事件不在接收的原因是什么,然后再去研究NestedScroll为什么能将滑动距离在子父view里去随意的分配,分配的原因是不是跟一些博客上说的,在onTouchEvent里面去处理的。如何处理的。