事件分发机制

200 阅读2分钟

1.Activity

Window(PhoneWindow)->DecorView(FrameLayout)->ViewGroup

  • dispatchTouchEvent
    • return true/false Touch事件被消费
    • return super 正常分发Touch事件->ViewGroup.dispatchTouchEvent
    • Window.superDispatchTouchEvent
      • DecorView.superDispatchTouchEvent
  • onTouchEvent
    • return true Touch事件被消费

2.ViewGroup

  • mFirstTouchTarget(TouchTarget) 事件被消费不为null
    • child(View) 被Touch的子View对象
    • pointerIdBits(int) 被触摸的ID
    • next(TouchTarget)下一级触摸目标
  • requestDisallowInterceptTouchEvent(boolean disallowIntercept)
    • ture 不允许拦截
    • false 允许拦截
  • dispatchTouchEvent

    • return false 不消费事件,往回(上级的onTouchEvent)传递,后续系列事件不再传递到此View
    • return true 事件被消费
    • return super 结果由onTouchEvent决定
    final boolean intercepted;
    if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
        if (!disallowIntercept) {
            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;
    }
    
    • 检查是否需要ViewGroup拦截Touch事件
      • ACTION_DOWN
      • mFirstTouchTarget!=null 表示ViewGroup没有拦截Touch事件并且子View消费了Touch
      • mFirstTouchTarget==null 表示ViewGroup拦截了Touch事件或者虽然ViewGroup没有拦截Touch事件但是子View也没有消费Touch。总之,此时需要ViewGroup自身处理Touch事件
     if (!canceled && !intercepted) {
         ...
         //此事件为Down事件或者第二个手指触碰屏幕事件或者为鼠标事件,此处只研究Donw事件
             if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                        ...
                      if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                         //1
                         ...
                      }
             }
         
     }
     ...
     if (mFirstTouchTarget == null) {
                // 2
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
     } else {
         ...
         //3
          if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
          } else {
              ...      
             if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
             }
             ...
         }
         ...
     }
    
    • dispatchTransformedTouchEvent
      • 事件DOWN且未被拦截会调用1,分发给孩子View处理
      • 事件未被孩子View消费会调用2,给当前onTouchEvent处理,后续不往下分发
      • Down事件被孩子View消费,后续事件会调用3;Down事件时不会被调用
      if (child == null) {
            handled = super.dispatchTouchEvent(event);
      } else {
            handled = child.dispatchTouchEvent(event);
      }
    
  • onInterceptTouchEvent

    • return false/super不拦截
    • return true拦截mFirstTouchTarget==null,触摸事件交给当前ViewGroup的onTouchEvent
  • onTouchEvent

    • return super/false 不消费事件,往回传递(上级的onTouchEvent),后续系列事件不再传递到此ViewGroup
    • return true 事件被消费

3.View

  • dispatchTouchEvent
     if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
         result = true;
     }
     if (!result && onTouchEvent(event)) {
         result = true;
     }
    
    • OnTouchListener不为空,且onTouch返回true,则事件被消费,不会传递至OnTouchEvent
    • return false 不消费事件,往回传递,后续系列事件不再传递到此View
    • return true 事件被消费
    • return super 结果由OnTouchListener.onTouch/onTouchEvent决定
  • onTouchEvent
    • return super/false 不消费事件,往回传递,后续系列事件不再传递到此View
    • return true 事件被消费

面试题

事件传递是由 Activity_diapatchTouchEvent——>Window:PhoneWindow_diapatchTouchEvent——>DecorView:FrameLayout_diapatchTouchEvent——>ViewGroup_diapatchTouchEvent(可通过拦截方法onInterceptTouchEvent停止传递)——...——>View_diapatchTouchEvent->View_onTouchEvent->ViewGroup_onTouchEvent-...->Activity_onTouchEvent。

通俗来说就是Activity收到事件,询问子View能否消费,子View为ViewGroup情况下,判断其是否拦截:

  • 若拦截交给自己的onTouchEvent消费;
  • 若未拦截,则继续询问到具体子View为止,最后交给View的onTouchEvent消费;
  • onTouchEvent:事件被消费,事件结束;事件未被消费,返回给上级onTouchEvent消费,往后序列事件将不再传递给该View处理