美文网首页
Android触摸事件分发流程

Android触摸事件分发流程

作者: BlueSocks | 来源:发表于2022-11-16 14:32 被阅读0次

当触摸事件发生时,Activity先接受到事件。

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    //在Activity可以实现该方法,处理必要的逻辑
    //当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
        onUserInteraction();
    }
   // 若返回true
    if (getWindow().superDispatchTouchEvent(ev)) {
     //1. 获取到的是window的唯一实现类PhoneWindow;
    //2.该方法由其唯一子类PhoneWindow类实现,
    // 3.PhoneWindow内部有DecorWindow类,其继承自FrameLayout,是所有界面的父类,
//4.FrameLayout继承自ViewGroup类,最终调用到ViewGroup的super.dispatchTouchEvent(event);
//实现从Activity到内部View的传递,即内部可优先处理事件,若未true,再由Activity处理。
        return true;
    }
   // 否则:继续往下调用Activity.onTouchEvent
    return onTouchEvent(ev);

DecorView内的dispatchToucEvent(可以通过Activity内的onTouchEvent打开DecorView)

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
 //cb.dispatchTouchEvent(ev) :调用Activity的dispatchTouchEvent()
// super.dispatchTouchEvent(ev):交给ViewGroup处理。
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);

关于Activity的内的onTouchEvent

最后当触摸屏事件未被其下的任何视图处理时调用,这对于处理发生在窗口边界之外的触摸事件非常有用,因为在那里没有接收它的视图。

public boolean onTouchEvent(MotionEvent event) {
   //处理发生在Window边界外的触摸事件。
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }

    return false;
}
// 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
    if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
            && isOutOfBounds(context, event) && peekDecorView() != null) {
      //说明事件在边界外,消费事件  
      return true;
    }

回到getWindow().superDispatchTouchEvent(ev)向内ViewGroup内传递的分析。

ViewGroup内dispatchTouchEvent

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
    }

    //如果事件以可访问且可聚焦的view为目标,则启动正常事件调度。否则交给子view处理点击。
    if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
        ev.setTargetAccessibilityFocus(false);
    }
    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) {
         //当开始一个新的触摸手势时,删除原先的状态。
         //重置状态,(由于应用程序切换、ANR或其他一些状态改变,底层可能已经取消事件)
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        //判断是否被打断
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
 //判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),
  //可通过调用requestDisallowInterceptTouchEvent()修改
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
// 对onInterceptTouchEvent()返回值取反
// a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),则为true
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action);
            } else {
// b. 若在onInterceptTouchEvent()中返回true(即拦截事件),则为false,
                intercepted = false;
            }
        } else {
            intercepted = true;
        }
        //.......此处省略部分代码
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        if (!canceled && !intercepted) {
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                    ? findChildWithAccessibilityFocus() : null;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
          //.......此处省略部分代码
                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
               //遍历所有的子view,寻找可以传递事件的view
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = getAndVerifyPreorderedIndex(
                                childrenCount, i, customOrder);
                        final View child = getAndVerifyPreorderedView(
                                preorderedList, children, childIndex);
                       //如果找到一个可达且可获取焦点的view,让其先获得事件,  
                    //如果没有处理,执行异常调度。在在给定的时间范围内,进行双重迭代,这是比较安全的。
                        if (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }
                            childWithAccessibilityFocus = null;
                            i = childrenCount - 1;
                        }
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }
                     //.......此处省略部分代码
                        resetCancelNextUpFlag(child);
              //将事件转换成特定子view的坐标空间,过滤掉无关的指针ID,并在必要时重写其事件。
             //如果子view为空,事件将转交给其父类处理。
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            //将事件分发给子view
                            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 = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                  //此处省略N行........
                    }
                    if (preorderedList != null) preorderedList.clear();
                }
               //mFirstTouchTarget:触摸目标链表中的第一次触摸目标。
                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    //若没有找到可以传递事件的子view,将指针分配到最近最少添加的view。
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }
        if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            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;
                    }
                 //此处省略N行........
                }
                predecessor = target;
                target = next;
            }
        }
        // Update list of touch targets for pointer up or cancel, if needed.
         //此处省略N行........
    return handled;
}

前面判断完分发条件后,对可以获取的view分发事件,主要是通过以下方法:

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) {
            //若子view为null,执行其父view的dispatchTouchEvent
            handled = super.dispatchTouchEvent(event);
        } else {
           //若不为null,则传递事件。
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
    // 计算要传递的指针的数量。
    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) {
        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;
}

View内的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {
    // 如果事件可被可达到且的焦点view处理
    if (event.isTargetAccessibilityFocus()) {
        if (!isAccessibilityFocusedViewOrHost()) {
            return false;
        }
        // view有焦点,且获得到事件处理
        event.setTargetAccessibilityFocus(false);
    }

    boolean result = false;
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(event, 0);
    }
    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        stopNestedScroll();
    }
    if (onFilterTouchEventForSecurity(event)) {
    //条件1:(mViewFlags & ENABLED_MASK) == ENABLED
      //a. 该条件是判断当前点击的view是否enable
      // b. 由于很多View默认enable,故该条件恒定为true
   //条件2:handleScrollBarDragging(event),事件是否是拖动滚动条
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        // 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()
  //     1. mOnTouchListener != null: 是否注册触摸监听
  //     2. (mViewFlags & ENABLED_MASK) == ENABLED: 当前被点击的view是否enable
  //     3. mOnTouchListener.onTouch(this, event) :监听的boolean类型回调值是否是true
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
         //符合条件,未被打断,则事件分发结束
            result = true;
        }
      //若为fasle,调用view的onTouchEvent()方法处理事件。
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    //此处省略N行代码..........
    return result;
}

再看onTouchEvent方法()

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 & ENABLED_MASK) == DISABLED) {
      //如当前view的状态是disable的
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        return (((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
    }
    if (mTouchDelegate != null) {
         //如果TouchDelegate对象不为空,将事件传递给它处理,不再往下。
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }
    if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
        switch (action) {
            case MotionEvent.ACTION_UP:
              //手指抬起action
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    //如果view没有获取焦点,为其请求焦点
                    boolean focusTaken = false;
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus();
                    }
                    if (prepressed) {
                        setPressed(true, x, y);
                   }
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        removeLongPressCallback();
                        if (!focusTaken) {  
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                           //执行点击事件
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }
                 //此处省略N行代码......
                break;

            case MotionEvent.ACTION_DOWN:
                mHasPerformedLongPress = false;
                if (performButtonActionOnTouchDown(event)) {
                    break;
                }
               //此处省略N行........
                if (isInScrollingContainer) {
                     //此处省略N行........
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                } else {
                    setPressed(true, x, y);
                   //判断是不是长按
                    checkForLongClick(0, x, y);
                }
                break;

            case MotionEvent.ACTION_CANCEL:
                //此处省略N行........
                break;

            case MotionEvent.ACTION_MOVE:
                //此处省略N行........
                break;
        }
        return true;
    }
    return false;
}
public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }

    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}

来自:https://www.yuque.com/jinrichenxiang/source/nwmqw6

相关文章

网友评论

      本文标题:Android触摸事件分发流程

      本文链接:https://www.haomeiwen.com/subject/xfbyxdtx.html