本文共 18271 字,大约阅读时间需要 60 分钟。
//正常情况下,触摸事件的各个状态都会执行此判断 ;因为大多数情况下,下面这两个条件 总有一个是成立的 if ( //如果是按下事件 actionMasked == MotionEvent.ACTION_DOWN || //不是按下事件,且记录了下一个可消费View(即 存在触摸目标) //此时正常情况下,事件来到了 Move 等状态 mFirstTouchTarget != null) { //查看子view是否允许父布局拦截 //此处如果是DOWN事件,则一定 允许拦截,因为 FLAG_DISALLOW_INTERCEPT 标记会在上面被重置 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; //当父布局允许拦截时,查看是否需要拦截 if (!disallowIntercept) { //通过onInterceptTouchEvent查看是否需要拦截 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } //当父布局不允许拦截时,则直接不拦截,不再关心onInterceptTouchEvent()方法的返回值 else { intercepted = false; } } //如果不是 DOWN 事件,且也没有 下一个触摸目标,则此时应该直接拦截 else { intercepted = true; }
//如果子View不可接受点击 || 点击的点不在子View的范围内,跳过此次循环...if ( //canReceivePointerEvents(): 确保子View可以接收事件 !child.canReceivePointerEvents() //isTransformedTouchPointInView : 如果子视图在转换到它的坐标空间时包含指定的点,则返回true。 || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue;}...
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); //不包含child,交由 父类(View类)处理 if (child == null) { handled = super.dispatchTouchEvent(event); } //包含child, 向child分发事件,并等待接收返回值 else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } //计算要交付的指针数量 final int oldPointerIdBits = event.getPointerIdBits(); final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; //在特殊情况下,有可能产生没有指针的触摸事件,如果发生了,则删除此事件 if (newPointerIdBits == 0) { return false; } //如果 新旧触摸 相同, 则不需要执行任何奇特的不可逆转换 (即 下面的这一步 :transformedEvent.transform(child.getInverseMatrix());) //只需要小心地恢复我们所做的任何更改,我们就可以为这个分派重用motion事件。而不必复制一份 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) { 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()); } //事件分发给child handled = child.dispatchTouchEvent(transformedEvent); } // Done. transformedEvent.recycle(); return handled; }
//将此View标记为触目目标(此事件集 的 下次事件 会直接找到此触目目标而无需遍历)newTouchTarget = addTouchTarget(child, idBitsToAssign);
@Override public boolean dispatchTouchEvent(MotionEvent ev) { //检验输入事件的一致性 if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click. if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } //定义最终的返回值 boolean handled = false; //过滤触摸事件, 确定触摸的view和window没有被遮挡 if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); //通过掩码方式重新生成该动作, 规避在代码执行该过程中 动作指针发生变化,确保当前处理的是一个触摸动作 final int actionMasked = action & MotionEvent.ACTION_MASK; //如果是 <按下的动作> ,则重置状态和标志 if (actionMasked == MotionEvent.ACTION_DOWN) { //通过mFirstTouchTarget ,为 记录的可消费触摸事件 的 view链 传递取消事件 ,并清空mFirstTouchTarget cancelAndClearTouchTargets(ev); //重置一些标记状态,比如 FLAG_DISALLOW_INTERCEPT 标记, 它会使之前设置的 "父布局是否拦截事件" 失效 resetTouchState(); } // 检查是否要拦截事件 final boolean intercepted; //正常情况下,触摸事件的各个状态都会执行此判断 ;因为大多数情况下,下面这两个条件 总有一个是成立的 if ( //如果是按下事件 actionMasked == MotionEvent.ACTION_DOWN || //不是按下事件,且记录了下一个可消费View(即 存在触摸目标) mFirstTouchTarget != null) { //查看子view是否允许父布局拦截 //此处如果是DOWN事件,则一定 允许拦截,因为 FLAG_DISALLOW_INTERCEPT 标记会在上面被重置 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; //当父布局允许拦截时,查看是否需要拦截 if (!disallowIntercept) { //通过onInterceptTouchEvent查看是否需要拦截 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } //当父布局不允许拦截时,则直接不拦截,不再关心onInterceptTouchEvent()方法的返回值 else { intercepted = false; } } //如果不是 DOWN 事件,且也没有 下一个触摸目标,则此时应该直接拦截 else { intercepted = true; } // If intercepted, start normal event dispatch. Also if there is already // a view that is handling the gesture, do normal event dispatch. if (intercepted || mFirstTouchTarget != null) { ev.setTargetAccessibilityFocus(false); } //记录是否是取消事件 (事件之前被取消了 || 事件本事是取消事件) final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // 查看是否需要在适当时将事件切分为多个 (多指操作) final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; //记录新的触摸目标 TouchTarget newTouchTarget = null; //标记已经 找到了新的 触摸目标 boolean alreadyDispatchedToNewTouchTarget = false; //如果事件没有被取消 && 没有被拦截 if (!canceled && !intercepted) { //事件是否存在可及的view , 如果存在则返回此View (此处与分析关联不大) View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; // DOWN事件 / 多指DOWN事件 / 悬浮针移动 // (可以理解为此处只是在处理Down事件 ,所以没有必要查看是否记录了触摸目标 mFirstTouchTarget) if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { //获取事件角标(当为down事件时,恒为0) final int actionIndex = ev.getActionIndex(); // always 0 for down //获取事件的指针id final int idBitsToAssign = split ? //如果是多指需要切分 1 << ev.getPointerId(actionIndex) //否则返回固定值 : TouchTarget.ALL_POINTER_IDS; //清除此 指针id 之前标识的 触摸目标,防止不同步问题 removePointersFromTouchTargets(idBitsToAssign); //记录子view的个数(也是为了防止多线程问题) final int childrenCount = mChildrenCount; // 如果存在子View (此处的newTouchTarget肯定为null) if (newTouchTarget == null && childrenCount != 0) { //获取触摸事件的坐标 final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); //创建一个包含特定排序的子视图的列表 (因为这在一个递归循环中,不应该直接创建对象,而是采用一个预留的数组,并在使用后清空它) final ArrayListpreorderedList = buildTouchDispatchChildList(); //标记是否具有自定义排序 final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); //记录children数组 final View[] children = mChildren; for (int i = childrenCount - 1; i >= 0; i--) { //安全的取出每一个子View final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); //如果存在视图 具有 可访问性焦点 ,我们应该首先处理它,并从子View中找到此视图 //如果不存在这种视图,则执行正常的事件分发工作 //此处与下方的子view的找寻工作是两套循环 if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } //如果子View不可接受点击 || 点击的点不在子View的范围内,跳过此次循环 if ( //canReceivePointerEvents(): 确保子View可以接收事件 !child.canReceivePointerEvents() //isTransformedTouchPointInView : 如果子视图在转换到它的坐标空间时包含指定的点,则返回true。 || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } //如果子View 可接受点击 且 坐标空间包含点击的坐标,将其转换为 触摸目标对象 //由于此处mFirstTouchTarget因为是Down事件被清空,所以此处 newTouchTarget应该为null newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { //如果 newTouchTarget 不为null,则说明此 子View 已经开始接收触摸事件 //此处终止循环, 并在它正在处理的指针之外,给它一个新的指针 newTouchTarget.pointerIdBits |= idBitsToAssign; break; } //重置子View被设置取消的标志 ; 如果之前真的设置过,则返回true resetCancelNextUpFlag(child); if (//对此 子View 执行正常的事件分发 dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) ) { //如果条件返回true,标明 存在child 消费事件 // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //将此View标记为触目目标(此事件集 的 下次事件 会直接找到此触目目标而无需遍历) newTouchTarget = addTouchTarget(child, idBitsToAssign); //表示已经找到了需要分发的目标 alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } 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; } } } // 如果mFirstTouchTarget 为null ,代表 此视图 不含有Child ,或者 Child没有消费事件, // 它在此处代替了 是否存在child消费事件的 布尔值 if (mFirstTouchTarget == null) { //如果mFirstTouchTarget 为null时,代表 此视图 不含有Child ,或者 Child没有消费事件,等同于一个图片 //此处的child传值很关键,它会使此布局直接调用了 super.dispatchTouchEvent(), handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } //如果mFirstTouchTarget不为null ,则说明存在 最终可消费事件的View链 并存储在了 mFirstTouchTarget 中 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; //迭代保存的 最终可消费事件的View链 while (target != null) { final TouchTarget next = target.next; //如果已经处理了并确定处理的是此视图,则跳过此次循环,直接返回true (在上方的Down事件中我们进行了处理) if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } //如果还没处理 (此处事件已经不是Down事件,或许是Move事件等), // 此时,我们应该直接通过记录的 触摸标记,直接找到下一级可消费事件的child else { //需要查看是否要取消事件 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; //通过 触摸标记 中记录的,直接找到下一个 可消费的child进行分发 if (dispatchTransformedTouchEvent(ev, cancelChild, //记录的可消费的View和它的指针id target.child, target.pointerIdBits)) { //如果可消费,继续向上层返回true handled = true; } //当事件取消时,mFirstTouchTarget指向了next(普通情况下为null); //即当move事件中当父布局开始拦截事件时,也会进行此次处理 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; } 按下的动作>
转载地址:http://iwamz.baihongyu.com/