Do note

The working process

  • Measure: Calculates the width and height of the view. The viewgroup calls the onMesure method of the child view to traverse the entire view tree.
  • Layout: Determines the position of the view in the parent container, similar to measure.
  • Draw: Draw the view on the screen, using dispathDraw to traverse the view tree.
    • Draw the background
    • Draw yourself
    • Draw the son view
    • Draw decorative

DecorView

The top-level view, which contains a LinearLayout, which contains a TitleBar inside, and a ViewGroup with a CONTENT ID, you can set the active content View using the setContentView method.

The event system

  • MotionEvent: This object records the X and y coordinates of a click event.

Event distribution mechanism

Before, we learned about view tree, that is, when you open an app on your phone, the screen in front of you contains a view tree, which can be understood as they are superimposed on each other. When you click on the screen, a MotionEvent object is generated that the Activity sends to the root of the View tree, and its dispatchTouchEvent method is called. In this method, some Settings are used to determine whether or not to intercept the event. Or call the dispatchTouchEvent method of the child View.

  • DispatchTouchEvent: Distribution logic that handles click events.
  • OnInterceptTouchEvent: Handles whether to intercept event logic and returns a Boolean value.
  • OnTouchEvent: Handles the click event logic.

This method is called when a click event occurs, and you can override the method to intercept the click event.

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

GetWindow ().superDispatchTouchEvent(ev) dispatches click events to Window objects. Moving on, you find that the PhoneWindow class is not directly accessible.

To find a solution, go to the android.jar file in the SDK directory, add as Library to the libs folder, and double-click Shift to find phoneWindow. class to see the source code.

You can see in the PhoneWindow source code that it calls the superDispatchTouchEvent method in the DecorView object.

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
Copy the code

You can see in the DecorView class that it invokes the dispatchTouchEvent method to enter the event distribution process.

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
Copy the code

In an event distribution mechanism, multiple events can form a sequence of events, and an event distribution mechanism is a set of code that processes this sequence of events. An event sequence typically begins with the MotionEvent.ACTION_DOWN action and ends with the MotionEvent.ACTION_UP action.

When the hit event is DOWN, the TouchTarget status is reset. If the previous event is handled by the View, there is an argument mFirstTouchTarget pointing to the child View. In the resetTouchState() method, the mFirstTouchTarget is copied back to NULL.

  // 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();
  }
Copy the code

If the hit event is DOWN or the mFirstTouchTarget is not null, that means the hit event is not in the initial state, and if the hit event is in the initial state, or if the sequence of events is not finished, it will be distributed in the if condition, Otherwise, set the Boolean variable intercepted to false. Use mGroupFlags to calculate whether to intercept the event. That is, after being intercepted, the sequence of events is also handled by the view.

// Check for interception.
final boolean intercepted;
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! =null) {
    final booleandisallowIntercept = (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;
}
Copy the code

Enter here without intercepting the event, traverse the child nodes, and find the view that can consume the event. You can see that if you click the event coordinates fall in the child element area

for (int i = childrenCount - 1; i >= 0; i--) {
    final int childIndex = getAndVerifyPreorderedIndex(
            childrenCount, i, customOrder);
    final View child = getAndVerifyPreorderedView(
            preorderedList, children, childIndex);
    if(! child.canReceivePointerEvents() || ! isTransformedTouchPointInView(x, y, child,null)) {
        continue;
    }

    newTouchTarget = getTouchTarget(child);
    if(newTouchTarget ! =null) {
        // Child is already receiving touch within its bounds.
        // Give it the new pointer in addition to the ones it is handling.
        newTouchTarget.pointerIdBits |= idBitsToAssign;
        break;
    }

    resetCancelNextUpFlag(child);
    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;
    }

    // The accessibility focus didn't handle the event, so clear
    // the flag and do a normal dispatch to all children.
    ev.setTargetAccessibilityFocus(false);
}
Copy the code

We can find this code below, in dispatchTransformedTouchEvent sub-elements dispatchTouchEvent method is invoked, end of event distribution.

  event.setAction(MotionEvent.ACTION_CANCEL);
  if (child == null) {
      handled = super.dispatchTouchEvent(event);
  } else {
      handled = child.dispatchTouchEvent(event);
  }
  event.setAction(oldAction);
  return handled;
Copy the code