View事件分發

事件分發

自定義ViewGroup 和 View

在自定義ViewGroup中打印三個事件方法

在自定義View中打印二個事件方法

在Activity中不給View 設置點擊事件

結果

ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch -> View onTouchEvent -> ViewGroup.onTouchEvent

沒有打印onClick 理解爲沒有消費事件

dispatchTouchEvent() 事件分發

dispatchTouchEvent()部分源碼

  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) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // Check for interception.
            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;
            }
    }

第一次按下會走進來

            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
        /**
     * Cancels and clears all touch targets.
     */
    private void cancelAndClearTouchTargets(MotionEvent event) {
        if (mFirstTouchTarget != null) {
            boolean syntheticEvent = false;
            if (event == null) {
                final long now = SystemClock.uptimeMillis();
                event = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
                syntheticEvent = true;
            }

            for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
                resetCancelNextUpFlag(target.child);
                dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
            }
            clearTouchTargets();

            if (syntheticEvent) {
                event.recycle();
            }
        }
    }

在clearTouchTargets()中,將mFirstTouchTarget ==null ;

/**
     * Clears all touch targets.
     */
    private void clearTouchTargets() {
        TouchTarget target = mFirstTouchTarget;
        if (target != null) {
            do {
                TouchTarget next = target.next;
                target.recycle();
                target = next;
            } while (target != null);
            mFirstTouchTarget = null;
        }
    }

所以在第一次進來 cancelAndClearTouchTargets(ev)會將mFirstTouchTarget設置爲null

             final boolean intercepted;
            //  第一次進來  actionMasked == MotionEvent.ACTION_DOWN 
            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;
            }

onInterceptTouchEvent(ev); 判斷是否需要攔截 默認返回false

 public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }

接着往下走

默認情況下是 true if 能夠執行  
if (!canceled && !intercepted) {   
    if (newTouchTarget == null && childrenCount != 0) { // DOWN 可以執行       
        for (int i = childrenCount - 1; i >= 0; i--) {  
        // 反序的for循環  獲取子 View child            
                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {  
                // 如果子 View 返回true 就會進來 主要給 mFirstTouchTarget = target; 賦值  
                 addTouchTarget(child, idBitsToAssign);                             
            }                   
        }      
}}
  private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }

dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)

該方法中核心代碼

 // Perform any necessary transformations and dispatch.
        if (child == null) {
           調用View的事件分發
            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());
            }
            調用View的事件分發
            handled = child.dispatchTouchEvent(transformedEvent);
        }

當子View沒有設置點擊事件,代表不消費事件,所以上面的child.dispatchTouchEvent()會返回false,所以handled = false,導致mFirstTouchTarget無法賦值還是爲Null,

第二次進來時 ,此時actionMasked = MotionEvent.ACTION_Move,並且 mFirstTouchTarget==null, 所以 intercepted = true,

        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;
            }

此時 canceled = false , intercepted = true ,所以不會調用子View的事件分發

  if (!canceled && !intercepted) {
        ....代碼省略  去調用子View的事件分發
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章