1. View tree loading process

When we call the startActivity() method, we call performLaunchActivity() in the ActivityThread to get an Activity instance, In the Instrumentation callActivityOnCreate() method call the Activity onCreate() to complete the creation of the DecorView. We then call handleResumeActivity() to call the Activity’s onResume() back and forth:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    / /...
    WindowManagerGlobal.initialize();
    // Create an instance of the Activity, where the callback to the Activity's onCreate() method is completed
    Activity a = performLaunchActivity(r, customIntent);
    if(a ! =null) {
        // ...
        // Call back the Activity's onResume() method here
        handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);if(! r.activity.mFinished && r.startsNotResumed) {// Completes the callback to the Activity's onPause() method here
            performPauseActivityIfNeeded(r, reason);
            // ...}}// ...
}
Copy the code

PerformResumeActivity () in the handleResumeActivity() method then calls back to the Activity’s onResume() method. In this method, we take the previously added DecorView from the Window and add it to the WindowManager:

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    // In this case, the Activity's onResume() is called back
    r = performResumeActivity(token, clearHide, reason);
    if(r ! =null) {
        final Activity a = r.activity;
        // ...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            // Get the DecorView here
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            // Get WindowManager instance, which is WindowManagerImpl
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // The Activity is rebuilt to reuse the DecorView to notify the child elements
                ViewRootImpl impl = decor.getViewRootImpl();
                if(impl ! =null) { impl.notifyChildRebuilt(); }}if (a.mVisibleFromClient) {
                if(! a.mWindowAdded) { a.mWindowAdded =true;
                    // Add the DecorView to the WindowManager
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
        }
    }
}
Copy the code

Here, WindowManager is an instance of WindowManagerImpl, and its addView() method is called using WindowManagerGlobal’s addView() method. In this method a ViewRootImpl is new and its setView() is called to add the passed DecorView to the Window. At the same time, requestLayout() is called for layout, and then performTraversals() is called to finish traversing the View tree:

private void performTraversals(a) {
    // ...
    if(! mStopped || mReportNextDraw) {// ...
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    // ...
    final booleandidLayout = layoutRequested && (! mStopped || mReportNextDraw);boolean triggerGlobalLayoutListener = didLayout
            || mAttachInfo.mRecomputeGlobalAttributes;
    if (didLayout) {
        performLayout(lp, mWidth, mHeight);
        // ...
    }
    // ...
    if(! cancelDraw && ! newSurface) {// ...performDraw(); }}Copy the code

This method calls performMeasure(), performLayout(), and performDraw(). They call the DecorView’s measure(), layout(), and draw() to measure, layout, and draw the entire View tree, and an interface is presented to the user. OnMeasure (), onLayout(), onDraw(), onMeasure(), onLayout() The last three methods are called by the first three, essentially providing the user with custom methods. Let’s take a look at what each of these methods does, but of course we’ll try to do it from the perspective of custom controls as much as possible, because that’s probably more helpful for a developer.

2, the measure ()

The size of the View is not only determined by itself, but also affected by the parent control. In order to better adapt to various situations, the control is generally measured by itself. Above we mentioned the measure() method, which is used to measure the size of the View, but the onMeasure() method does most of the measuring. In the View, onMeasure() is a protected method, which is clearly designed to provide the child View with control of its own size according to the constraints provided by the parent container, so as to realize its own size measurement logic. So, when we customize a control, only the onMeasure() method is overridden, not the measure() method.

In Android, our controls are divided into View and ViewGroup types. According to the above analysis and the measurement of View, we can draw the following conclusions: In Android, the ViewGroup encapsulates constraints as widthMeasureSpec and heightMeasureSpec parameters to child elements, depending on the layout characteristics of the ViewGroup. It then resizes itself in the child element based on these two parameters. So, a ViewGroup’s measure() method can vary depending on its layout properties; A View’s measure(), regardless of its parent, is determined only by widthMeasureSpec and heightMeasureSpec.

Let’s look at the different representations of onMeasure() in views and viewgroups.

2.1 the View of onMeasure ()

Here is the onMeasure() method in the View class. This is a default implementation that calls the setMeasuredDimension() method to store the measured width and height. When customizing the View, we also need to call setMeasuredDimension() to store the final measurement:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(
        getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
Copy the code

Obviously, our measurement is based on the widthMeasureSpec and IghtMeasurespec parameters. They are integer, 32-bit variables that contain information about the measurement mode and value (stored bitwise on an integer variable, packaged as an integer to save storage). Normally we would get the measurement mode and the measurement value of the height and width separately as follows:

int widthsize = MeasureSpec.getSize(widthMeasureSpec);      // Measure the value
int widthmode = MeasureSpec.getMode(widthMeasureSpec);      // Measurement mode
int heightsize = MeasureSpec.getSize(heightMeasureSpec);    // Measure the value
int heightmode = MeasureSpec.getMode(heightMeasureSpec);    // Measurement mode
Copy the code

The measurement modes are UNSPECIFIED, MeasureSpec.AT_MOST, and MeasureSpec.EXACTLY, which correspond to the binary values 00, 01, and 10 respectively.

  1. UNSPECIFIED: Default value, the parent control does not give any restrictions to the child View, the child View can be set to any size;
  2. EXACTLY: indicates that the parent control has specified the exact size of the child View.
  3. AT_MOST: indicates that there is no size limit for the child View, but there is an upper limit. The upper limit is usually the size of the parent View.

I’m not going to go into the details of the implementation of the default measurement logic in View. The general logic is as follows: first we get the default width or height using getDefaultSize(), which takes two parameters, one the default size and one the measurement mode. If the parent control doesn’t put any restrictions on it, it uses the default size, otherwise it uses the measured value. The default size by getSuggestedMinimumHeight ()/getSuggestedMinimumWidth () method, it will be according to the background image height/width and mMinHeight/mMinWidth value, Take a maximum value for the height/width of the control.

So, the View’s default measurement logic actually works like this: First, the View’s size is affected by the parent, and if the parent doesn’t limit it, it will take the maximum value of the background image and the minimum height or width as its size.

2.2 the ViewGroup onMeasure ()

Methods in 2.2.1 ViewGroup

Since the ViewGroup itself has no layout characteristics, it does not override onMeasure(). LinearLayout and RelativeLayout all overwrite and implement this method. Nonetheless, the ViewGroup provides a few methods to help us make measurements, starting with the measureChildren() method:

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < size; ++i) {
        final View child = children[i];
        if((child.mViewFlags & VISIBILITY_MASK) ! = GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); }}}Copy the code

If the specified View is in the GONE state, call measureChild() : measureChild() {measureChild();

protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
Copy the code

The method is also easier to understand by taking the layout parameters of the child element, LayoutParams, getting its width and height, and passing all the information to getChildMeasureSpec(). This gives you the childWidthMeasureSpec and childHeightMeasureSpec parameters for the child element layout. The child element’s measure() method is then called to traverse the entire View tree in turn. Let’s look at what the getChildMeasureSpec() method does:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    // Get the measurement mode and value of the parent control from the spec
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    // We need to ensure that size cannot be negative, i.e. the maximum space reserved for the child element, which is obtained from the measurement value of the parent element minus the fill
    int size = Math.max(0, specSize - padding);
    // The value to be returned
    int resultSize = 0;
    int resultMode = 0;
    // According to the parent space measurement mode
    switch (specMode) {
        // The size of the parent control is fixed
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                // The subview specifies the size
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // The child element wants to be the same size as the parent control (to fill the entire parent control)
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // The child element wants its own size, but cannot be larger than the parent control
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
        // There is no size limit to the size of the parent control, but there is an upper limit
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // The subview specifies the size
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // The child control wants to be the same size as the parent control, but the size of the parent control is also uncertain, so let the child control not larger than the parent control
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // The child controls want to determine their own size, limiting it to no larger than the parent control
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
        // The parent control has no restrictions and can be set to any size
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // The child element is set to size
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // The child controls want to be the same size as the parent controls, but the size of the parent controls is not determined; System 23 returns true for below and size for above
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
    }
    // Returns a encapsulated measurement result that encapsulates the measurement value and measurement mode into a 32-bit integer
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code

We have commented this code in great detail above. It only needs to be noted that the measurement result of the child element is based on the measurement result of the parent control. It is necessary to divide the measurement mode and measurement value of the parent element into the above nine cases combined with its own layout characteristics. Or it can be divided into the following cases as follows:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec), specSize = MeasureSpec.getSize(spec);
    int size = Math.max(0, specSize - padding);
    int resultSize = 0, resultMode = 0;
    if (childDimension >= 0) {
        // The size of the child element is used when the child element is specified
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
    } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
        // The child element wants to be the same size as the parent control
        if (specMode == MeasureSpec.EXACTLY || specMode == MeasureSpec.AT_MOST) {
            resultSize = size;
            resultMode = specMode;
        } else if (specMode == MeasureSpec.UNSPECIFIED) {
            // API23 is 0. When the parent control does not specify the size, the child control can only be 0. Above is the size
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0: size; resultMode = MeasureSpec.UNSPECIFIED; }}else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
        // The child element wants to determine its own size. Set its size up to the parent control's size
        if (specMode == MeasureSpec.EXACTLY || specMode == MeasureSpec.AT_MOST) {
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (specMode == MeasureSpec.UNSPECIFIED) {
            // API23 is 0. When the parent control does not specify the size, the child control can only be 0. Above is the size
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0: size; resultMode = MeasureSpec.UNSPECIFIED; }}return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code

These two methods are only divided from the same Angle, the latter method is considered from the child layout parameters. In addition, there is a sUseZeroUnspecifiedMeasureSpec Boolean parameters need to mention, depending on the version of the system to carry out the assignment:

    sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < Build.VERSION_CODES.M;
Copy the code

This parameter is true if the system is currently under the API23. The reason for adding this parameter is that, while the measurement mode of the parent control is UNSPECIFIED, the child element provides the parent control with an UNSPECIFIED size. Here’s what the notes say; -)

// In M and newer, our widgets can pass a "hint" value in the size
// for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers
// know what the expected parent size is going to be, so e.g. list items can size
// themselves at 1/3 the size of their container. It breaks older apps though,
// specifically apps that use some popular open source libraries.
Copy the code

2.2.2 LinearLayout onMeasure ()

Here we take a look at the LinearLayout of a standard container-type control and see how it implements its measurement logic.

The following is its onMeasure() method, which obviously realizes the measurement logic respectively according to the direction of its layout during measurement:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else{ measureHorizontal(widthMeasureSpec, heightMeasureSpec); }}Copy the code

Then, take measureVertical() as an example to see how the LinearLayout measures vertically. This code is quite long, so we’ll just take a portion of it for analysis:

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    // ...
    // Get the measurement mode of the LinearLayout
    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    // ...
    mTotalLength += mPaddingTop + mPaddingBottom;
    int heightSize = mTotalLength;
    heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
    // ...
        for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);
            if (child == null || child.getVisibility() == View.GONE) {
                continue;
            }
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final float childWeight = lp.weight;
            if (childWeight > 0) {
                // ...
                // Get a measurement value and measurement mode
                final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        Math.max(0, childHeight), MeasureSpec.EXACTLY);
                final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
                        lp.width);
                // Call the child element to measure
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                childState = combineMeasuredStates(childState, child.getMeasuredState()
                        & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
            }
            
            final int margin =  lp.leftMargin + lp.rightMargin;
            final int measuredWidth = child.getMeasuredWidth() + margin;
            maxWidth = Math.max(maxWidth, measuredWidth);

            booleanmatchWidthLocally = widthMode ! = MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT; alternativeMaxWidth = Math.max(alternativeMaxWidth, matchWidthLocally ? margin : measuredWidth); allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;final int totalLength = mTotalLength;
            // Increase the width to mTotalLength
            mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                    lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
        }
        mTotalLength += mPaddingTop + mPaddingBottom;
    // ...
    maxWidth += mPaddingLeft + mPaddingRight;
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
    // Finally determine the size of the measurement
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            heightSizeAndState);
    // ...
}
Copy the code

The above is the process of measuring the LinearLayout in the vertical direction. During the measurement, the measurement height of the child element will be added to the mTotalLength according to the layout of the child element, and then the filling size will be added as the final measurement result.

3, the layout ()

Layout() is used to determine the position of the control. It provides onLayout() for the word class to implement, as well as the onLayout() method for customizing the control. We do not need to override the onLayout() method if we are defining a View that is not of ViewGroup type.

Let’s look at the layout() method in View.

public void layout(int l, int t, int r, int b) {
    if((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) ! =0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
        // ...
    }

    // ...
}
Copy the code

The setFrame() method is called, which compares the new layout parameter with the old one to determine whether the size of the control has changed. If so, the invalidate() method is called and true is passed to indicate that the drawing cache has changed. The implementation of this method is not shown here. Then notice that the onLayout() method is called back in the Layout() method to determine the position of each control.

For the ViewGroup, it overrides layout() and calls the Layout () method in the View, but doesn’t do much logic overall. Like the measurement process, the ViewGroup does not implement the onLayout method. OnLayout (); onLayout(); onLayout();

Similar to the measurement process, the LinearLayout is divided into two situations according to the direction of the layout:

protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mOrientation == VERTICAL) {
        layoutVertical(l, t, r, b);
    } else{ layoutHorizontal(l, t, r, b); }}Copy the code

Again, let’s take the vertical approach. Compared with the measurement process, the layout process is much simpler and clearer:

void layoutVertical(int left, int top, int right, int bottom) {
    // ...
    // Get the top position according to the gravity of the control
    switch (majorGravity) {
       case Gravity.BOTTOM:
           childTop = mPaddingTop + bottom - top - mTotalLength;
           break;
       case Gravity.CENTER_VERTICAL:
           childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
           break;
       case Gravity.TOP:
       default:
           childTop = mPaddingTop;
           break;
    }

    // Iterate over the child control
    for (int i = 0; i < count; i++) {
        final View child = getVirtualChildAt(i);
        if (child == null) {
            childTop += measureNullChild(i);
        } else if(child.getVisibility() ! = GONE) {final int childWidth = child.getMeasuredWidth();
            final int childHeight = child.getMeasuredHeight();

            final LinearLayout.LayoutParams lp =
                    (LinearLayout.LayoutParams) child.getLayoutParams();

            int gravity = lp.gravity;
            if (gravity < 0) {
                gravity = minorGravity;
            }
            final int layoutDirection = getLayoutDirection();
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
            // Get the left position of the child control
            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.CENTER_HORIZONTAL:
                    childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                            + lp.leftMargin - lp.rightMargin;
                    break;
                case Gravity.RIGHT:
                    childLeft = childRight - childWidth - lp.rightMargin;
                    break;
                case Gravity.LEFT:
                default:
                    childLeft = paddingLeft + lp.leftMargin;
                    break;
            }

            if (hasDividerBeforeChildAt(i)) {
                childTop += mDividerHeight;
            }

            childTop += lp.topMargin;
            // Essentially calls the layout() method of the child controlsetChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child, i); }}}Copy the code

Since the layout is vertical, the position of the top of the child elements is calculated before traversing the child elements, and then the top height is superimposed, and finally the setChildFrame() method is called:

private void setChildFrame(View child, int left, int top, int width, int height) {
    child.layout(left, top, left + width, top + height);
}
Copy the code

This completes the call to the Layout () method of the entire View tree.

4, the draw ()

The logic of View’s draw() method implementation is also clear. During the drawing process, the following steps will be followed:

  1. Draw the background
  2. Save the canvas
  3. Draw its own content
  4. Draw child control
  5. Draw the faded edges of the View, such as a shadow effect
  6. Draw decorations, like scroll bars

The View provides the onDraw() method to draw its own content, so we can override this method when we customize the View. When we want to customize a ViewGroup control, we generally do not need to override the onDraw() method because it simply iterates through the child controls and calls their draw() methods in turn. (Of course, if you have to, you can.)

Here is the code, and the logic for each step is detailed in the comments:

public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
            (mAttachInfo == null| |! mAttachInfo.mIgnoreDirtyState); mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;// Step 1, draw the background, if needed
    int saveCount;

    if(! dirtyOpaque) { drawBackground(canvas); }// skip step 2 & 5 if possible (common case)
    final int viewFlags = mViewFlags;
    booleanhorizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) ! =0;
    booleanverticalEdges = (viewFlags & FADING_EDGE_VERTICAL) ! =0;
    if(! verticalEdges && ! horizontalEdges) {// Step 3, draw the content
        if(! dirtyOpaque) onDraw(canvas);// Step 4, draw the children
        dispatchDraw(canvas);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if(mOverlay ! =null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);

        // Step 7, draw the default focus highlight
        drawDefaultFocusHighlight(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }

    // ...
}
Copy the code

Notice that in the above method the dispatchDraw(Canvas) method is called to distribute draw events to child controls to complete the drawing of the View tree. In the View, this is an empty method that the ViewGroup overrides and calls drawChild() to complete the call to the specified View’s draw() method: drawChild(); drawChild(); drawChild(); drawChild(); drawChild()

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}
Copy the code

For LinearLayout, which itself has no drawing requirements, there is no overwriting onDraw() and dispatchDraw(Canvas) methods, because the functions provided in View and ViewGroup are enough to use.

Conclusion:

Above, we described how the entire View tree works on Android, from the DecorView being loaded into the window to the measurement, layout, and drawing methods. Essentially the entire workflow is a depth-first traversal of the View tree.