美文网首页
UI事件分发机制(上层)

UI事件分发机制(上层)

作者: 天上飘的是浮云 | 来源:发表于2022-04-03 11:05 被阅读0次

之前跟踪了下,手指点击屏幕。点击事件从底层传到App的Activty的过程。今天,来跟踪和理解下Android上层的事件分发机制和策略。

一、事件的L型链和U型链

L型链:消费了事件
L型链
U型链:没有消费事件
U型链

二、设计事件分发考虑的点:

  • 1、Activity只需要分发事件,它并不需要处理事件:dispatchTouchEvent是主责,onTouchEvent是兜底副业。
  • 2、View主要是处理事件,因为它是所有View的标杆,ViewGroup也继承至它,所以也有dispatchTouchEvent方法,主要是分发给自己的onTouchEvent处理。
  • 2、ViewGroupe继承至View,主要负责往下分发事件,子View不处理时,也可交由它自己处理。还有一个View没有的功能就是它可以拦截事件。

三、ViewGroup的dispatchTouchEvent方法

在ViewGroup中主要是onInterceptTouchEvent和dispatchTouchEvent起作用。

onInterceptTouchEvent主要是拦截事件
dispatchTouchEvent主要分发事件

public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
    boolean handled = false;
    if (onFilterTouchEventForSecurity(ev)) {
        ...
        // Check for interception.
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
2609行                   intercepted = onInterceptTouchEvent(ev);
        ...
2634行         if (!canceled && !intercepted) {

            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 View[] children = mChildren;
                    //1. 从上到下,倒序遍历,也就是说最上层优先的到点击事件
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        ...
                        //2. 判断点击事件的Point是否在子View的区域范围内
                        // (其实就是看point是否在left/right/top/bottom范围内);
                        if (!child.canReceivePointerEvents()
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }
                        ...
                        //找到子view能接收到Down事件后,调用dispatchTransformedTouchEvent方法
                        // 看子View是否处理事件,如果处理,则将子View添加到TouchTarget里。
                        // 并赋给mFirstTouchTarget。以便下次Move或Up事件时,
                        // 不需要再次遍历寻找子View。
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            ...
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                        ...
                }
                ...
            }
        }

        // Dispatch to touch targets.
2739行      if (mFirstTouchTarget == null) {
            // 被拦截或者没有子View处理通过dispatchTransformedTouchEvent
            // 交给自己onTouchEvent处理
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            ...
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                //move、up事件直接分发,不在寻找
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                }
            }
        }
    ...
    return handled;
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
...
       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);
        }
...
}
    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }

dispatchTouchEvent中有几个关键点:

  • 1、onInterceptTouchEvent返回true即被拦截,直接到2739行,而mFirstTouchTarget也==null,所以会直接调用dispatchTransformedTouchEvent(ev, canceled, null,
    TouchTarget.ALL_POINTER_IDS)交由自己的onTouchEvent处理。

  • 2、如果没有拦截会进入到2634行的if代码块中,这里将会做四件事:1、必须是Down事件才会进;2、需要将它的ziView按Z轴由上到下,倒序遍历,寻找能接收事件的子View;3、判断点击事件的Point是否在子View的区域范围内(其实就是看point是否在left/right/top/bottom范围内);4、找到子view能接收到Down事件后,调用dispatchTransformedTouchEvent方法看子View是否处理事件,如果处理,则将子View添加到TouchTarget里。并赋给mFirstTouchTarget。以便下次Move或Up事件时,不需要再次遍历寻找子View。

  • 3、用handled变量来接收View是否处理事件。

四、View的dispatchTouchEvent和onTouchEvent方法

View中的dispatchTouchEvent主要是分发给自己来处理事件。

  • 1、13424行代码,是View设置onToucheLisener时,如果onTouch方法返回FALSE,表示不拦截onTouchEvent方法和onClickListener;如果onTouch方法返回ture,表示拦截调onTouchEvent方法,onClickListener是在onTouchEvent中调用,所以也不会调用了 。
13424行 if (li != null && li.mOnTouchListener != null
            && (mViewFlags & ENABLED_MASK) == ENABLED
            && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
       }

13430行    if (!result && onTouchEvent(event)) {
                result = true;
            }
  • 2、onTouchEvent时,up事件中performClickInternal -> performClick -> li.mOnClickListener.onClick(this); 这是调用onClickListener的调用链

相关文章

网友评论

      本文标题:UI事件分发机制(上层)

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