Android View event shunt source code

outline

  • What are the events distributed by the event and what methods are involved
  • How event distribution is passed from Activity to View
  • View event distribution
  • ViewGroup event distribution

1. What are the events distributed by the event and what methods are involved

  • When the user touches the screen, an event will be generated. There are many static constants in this class for MotionEvent in android:

Name value meaning ACTION_DOWN0 The pressed gesture has started, and the action includes the initial start position. ACTION_UP1 After the press gesture is completed, the movement includes the final release position and all intermediate points since the last down or move event. ACTION_MOVE2A change has happened during a press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP} )ACTION_CANCEL3 The current gesture has been aborted.

  • By default, event distribution will be distributed in the order from Activity to ViewGroup to View
  • The event delivery process is from PhoneWindow->DecorView (mDecor is the View returned by getWindow().getDecorView(), and the View set by setContentView is the child View of the View), and then passed to the View tree of the Activity.

1. Important methods of View event distribution: diapatchTouchEvent and touchEvent, onClick

|---View
|   |---boolean dispatchTouchEvent(MotionEvent event)
|   |--- boolean onTouchEvent(MotionEvent event)

1.1 dispatchTouchEvent: If the event is handled by the view, it is true, otherwise it is false.

public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            // 重点 判断listenerInfo 中是否有事件listenter 并且mOnTouchListener.onTouc 返回true 
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
						// 第二个重点,如果上述判断为false,并且 onTouchEventr返回为true,消耗事件
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

  1. dispatchTouchEvent中final int actionMasked = event.getActionMasked();
  2. ListenerInfo: encapsulate all the listener information of the view into an object.
  3. There are two judgments in this method:
 ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
复制代码

If onTouchEvent or onTouchListenner is true, then the matter will be spending parts, meanwhile, is where is gone onTouchEvent

  • boolean onTouchEvent
  • MotionEvent.ACTION_UP: Called PerformClick()

Three questions:

1. Called in activity

 mTouchView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("TAG", "onTouch: " + event.getAction());
                return false;
            }
        });
        mTouchView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("TAG", "onClick: " );
            }
        });

     /***********TabLayout************/   
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TAG", "onTouchEvent: "+event.getAction());
        return super.onTouchEvent(event);
    }
复制代码

Order of execution:

2. What if it is a direct return ture in Tablyout?

Do not execute onClick, because onClick is in ACTION_UP in View.OntouchEvent, if you directly return true, you will not go to the super method.

3. What if dispatchEvent return true?

Do nothing

Distribution process

super.dispatchEvent->ListenerInfo->super.onTouchEvent(event)->ACTION_UP->performListener().
复制代码

2. ViewGroup event distribution:

The first is actionDown:

 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;
                }
            }
复制代码
  • mFirstTouchTarget: This is very important. DisallowIntercept will be called only when it is pressed for the first time. If you want to change the effect later, this is a pit.
|---ViewGroup
|   |---dispatchTouchEvent
|   |   |---onInterceptTouchEvent(ev)

There are situations where the View consumes time (for example, click, onTouchListener)

  • Press for the first time (down)—>ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent(ev)—>view.dispatchEvent—(the set of view)

  • The second time comes in (move) ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent(ev)—>view.dispatchEvent—(the set of view)

  • The third (up) ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent(ev)—>view.dispatchEvent—(the set of view)—>view.onClick


There is no consumption event in the child View. Only go once

  • Coming in for the first time (down) ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent(ev)—>view.dispatchEvent—view.onTouch—>onTouch Event

Source code analysis of viewGroup

actionDown

 /**
     * 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; //清除target
        }
    }
<!--------------------------------------------------->
      if (!canceled && !intercepted) // intercepted 默认是没有拦截
      {
         if (newTouchTarget == null && childrenCount != 0) {
              for (int i = childrenCount - 1; i >= 0; i--) {//反序列的for循环 先拿最上面的layout
                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                }
              }
         }
      }


/**
     * Transforms a motion event into the coordinate space of a particular child view,
     * filters out irrelevant pointer ids, and overrides its action if necessary.
     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
     */
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // Calculate the number of pointers to deliver.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        // If for some reason we ended up in an inconsistent state where it looks like we
        // might produce a motion event with no pointers in it, then drop the event.
        if (newPointerIdBits == 0) {
            return false;
        }
        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);

                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }

        // Perform any necessary transformations and dispatch.
        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);
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }

Finally, in order to help you understand the principles of Android-related knowledge points and interview-related knowledge, here are the relevant analysis of Tencent, ByteDance, Ali, and Baidu 2019-2020 interviews that I have collected and compiled . I have organized the technical points into the video and PDF (in fact, than expected to spend a lot of time), contains the knowledge context + many details .

The detailed arrangement can be seen on GitHub;

Android architecture video + BAT interview topic PDF + Kotlin entry to proficiency, Flutter entry to actual combat study notes

Core notes separated version directory.png

Guess you like

Origin blog.csdn.net/A_pyf/article/details/113866084