美文网首页
Android事件分发机制源码分析

Android事件分发机制源码分析

作者: 大佬的上半生 | 来源:发表于2019-11-27 18:43 被阅读0次
    • 一个事件时事件分发是从 Activity开始的(Activity.dispatchTouchEvent())
      来自这里的图片
      image.png
    • Android事件分发总是先传递到ViewGroup、再传递到View
      image.png
      当用户点击时先调用的是Activity的Activity.dispatchTouchEvent()
      image.png
    源码分析

    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

    相关文章

      网友评论

          本文标题:Android事件分发机制源码分析

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