Android事件分发机制源码分析

2019-11-27  本文已影响0人  大佬的上半生
源码分析

1.Activity.dispatchTouchEvent()



 public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

onUserInteraction:

每当Key,Touch,Trackball事件分发到当前Activity就会被调用。如果你想当你的Activity在运行的时候,能够得知用户正在与你的设备交互,你可以override该方法。
这个回调方法和onUserLeaveHint是为了帮助Activities智能的管理状态栏Notification;特别是为了帮助Activities在恰当的时间取消Notification。
所有Activity的onUserLeaveHint 回调都会伴随着onUserInteraction。这保证当用户相关的的操作都会被通知到,例如下拉下通知栏并点击其中的条目。
注意在Touch事件分发过程中,只有Touch Down 即Touch事件的开始会触发该回调,不会在move 和 up 分发时触发(从Activity 源码中 dispatchTouchEvent 方法中确实是这么做的)。

onUserLeaveHint:

作为Activity的生命周期回调的部分,会在用户决定将Acitivity放到后台时被调用。例如:当用户按下Home键,onUserLeaveHint就会被调用。但是当来电话时,来电界面会自动弹出,onUserLeaveHint就不会被调用。当该方法被调用时,他会恰好在onPause调用之前。

若将getWindow().superDispatchTouchEvent(ev)设置为ture,不会执行下去,也不会分发下去

image.png
2.PhoneWindow.superDispatchTouchEvent(MotionEvent event)
   @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

mDecor这个就是DecorView,布局最顶层,也就是说明这个事件分发到了最顶层

3.ViewGroup.dispatchTouchEvent(MotionEvent event)
仅贴出关键代码

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
 //1.ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
  // a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
   // b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
                    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;
            }
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            // 2.通过for循环,遍历了当前ViewGroup下的所有子View
            if (!canceled && !intercepted) {
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // 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();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                            ev.setTargetAccessibilityFocus(false);
                        }
          //3.判断是否点击的区域是否在子View范围类
               if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                    }
            }
          // 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复onInterceptTouchEvent()//从而让其返回true)   
         // 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
         // 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->>   //onClick(),即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的  //View.dispatchTouchEvent())
            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;
                    }
                }
            }
        return handled;
    }

1.ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),从而分发给子类
b. 若在onInterceptTouchEvent()中返回true(即拦截事件),从而自己处理不会分发给子类
2.通过for循环,遍历了当前ViewGroup下的所有子View
3.判断是否点击的区域是否在子View范围类
若点击的是空白处(即无任何View接收事件)拦截事件(手动复onInterceptTouchEvent(从而让其返回true)
调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递

总结流程图
image.png
上一篇下一篇

猜你喜欢

热点阅读