An overview,

Following the previous Android Application launch process analysis and source code call inquiry, from the start of the Activity to the formal start of the drawing process, the whole process is shown in the figure below:

  • I’ve listed the important method steps in the figure above, which you can see include the creation time of PhoneWindow and the initialization time of DecorView. Because PhoneWindow and DecorView play an important role in the Android View hierarchy, take a look at the following figure to understand when both are created and initialized and to get a better understanding of the overall View drawing process.
  • PhoneWindow is the most basic windowing system in Android. Each Activity creates one and is the real controller of the view. DecorView is essentially a FrameLayout that is the ancestor of all views in the Activity, the root node of the current Activity View.

Two, View draw source tracking

From STEP 14, the last STEP of the Android Application launch process analysis and its source call inquiry, we saw that the main thread received the h.launch_activity message, This is handled by the ActivityThread#handleLaunchActivity() method. This method then calls the performLaunchActivity() and handleResumeActivity() methods, described below.

ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String Reason) {··· Activity a = performLaunchActivity(r, customIntent);if(a ! = null) {handleResumeActivity(r.token,false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
            ···
            }
        } else{...}}Copy the code

2.1 performLaunchActivity ()

The performLaunchActivity() method performs several operations. Create an Activity object, call Activity#attach(), create a PhoneWindow object, call Activity#onCreate(), Initialize the DecorView, add the layout to the DecorView’s content, and call Activity#onStart();

Private Activity performLaunchActivity(ActivityClientRecord R, Intent customIntent) {··· Activity Activity = null; Try {... / / create the Activity object Activity = mInstrumentation newActivity (cl, component getClassName (), r.i ntent); Catch (Exception e) {···} try {···if(activity ! = null) {··· // Call the Activity#attach() to create a PhoneWindow objectactivity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); ·· // Call the Activity#onCreate() initializes the DecorView and adds the layout to the DecorView's content
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else{ mInstrumentation.callActivityOnCreate(activity, r.state); }...if(! R.tivity. MFinished) {// Call Activity#onStart()activity.performStart(); ···} catch (SuperNotCalledException e) {···} catch (Exception E) {··}return activity;
    }
Copy the code

The creation time of the PhoneWindow object is in the attach() method executed by the activity.

Activity.java

final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback ActivityConfigCallback) {··· // Create the PhoneWindow object mWindow = new PhoneWindow(this, window, activityConfigCallback); ··· // setWindowManager mwindow.setwindowmanager for PhoneWindow ( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); ··· // Associate PhoneWindow with Activity mWindowManager = mWindow.getwinDowManager (); ...}Copy the code

Next, the ActivityThread calls the Activity#onCreate() method. We know that the Activity’s onCreate() method calls setContentView(), The actual implementation of the Activity’s setContentView() comes from PhoneWindow. You can see that the getWindow() method returns the same mWindow object assigned by attach() above.

Activity.java

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
Copy the code

Let’s take a look inside PhoneWindow:

PhoneWindow.java

    @Override
    public void setContentView(int layoutResID) {
        
        if(mContentParent == null) {// Initialize DecorView installDecor(); }else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            ···
        }

        if(hasFeature (FEATURE_CONTENT_TRANSITIONS)) {...}else{// Add the layout to the DecorView's content mlayoutInflater.inflate (layoutResID, mContentParent); } ···} Private voidinstallDecor() {...if(mDecor == null) {// Create DecorView object mDecor = generateDecor(-1); ...}else{...}if(mContentParent == null) {mContentParent = generateLayout(mDecor); ...}}Copy the code

The above mContentParent is the outermost parent of the layout file layout. XML set in the Activity’s setContentView, and the layout part of the ContentView in DecroView.

2.2 handleResumeActivity ()

The handleResumeActivity() method performs two operations, calling Activity#onResume() to add the DecorView to the WindowManager.

ActivityThread.java

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {··· // Call the Activity#onResume()
        r = performResumeActivity(token, clearHide, reason);

        if(r ! = null) {...if (r.window == null && !a.mFinished && willBeVisible) {
                ···
                if (a.mVisibleFromClient) {
                    if(! a.mWindowAdded) { a.mWindowAdded =true; // Add the DecorView to WindowManager, // The WindowManager implementation class is WindowManagerImpl, WindowManagerImpl addView method wm.addView(decor, l); }else{···}}}else if(! WillBeVisible) {···} ···}else{...}}Copy the code

WindowManagerImpl.java

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
Copy the code
  • Let’s look at the concept of ViewParent and DecorView. ViewParent corresponds to the ViewRootImpl class. It is the link between WindowManager and DecorView. All three processes of drawing a View are done through ViewParent. In the ActivityThread, when the Activity object is created, the DecorView is added to the Window and the ViewRootImpl object is created. We associate the ViewRootImpl object with the DecorView and give the DecorView instance object to the ViewRootImpl to draw the View. Finally, call the ViewRootImpl classperformTraversals()In order to achieve view drawing. Let’s look at Windows ManagerGlobaladdView()Methods.

WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params, Display display, Root = new ViewRootImpl(view.getContext(), synchronized ()); display); ·· try {// load the DecorView into the Window root.setView(view, wparams, panelParentView); } catch (RuntimeException e) {···}}}Copy the code

Next, we go into the ViewRootImpl source code and continue tracking:

ViewRootImpl.java

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if(mView == null) { mView = view; ··· Schedule the first layout-before-adding to the window // Manager, to make sure wedothe relayout before receiving // any other events from the system. requestLayout(); ·· // Associate ViewRootImpl object with DecorView view.AssignParent (this); ···}} @override public voidrequestLayout() {
        if(! MHandlingLayoutInLayoutRequest) {... scheduleTraversals (); } } voidscheduleTraversals() {
        if(! MTraversalScheduled) {... mChoreographer. PostCallback (Choreographer. CALLBACK_TRAVERSAL mTraversalRunnable, null); ···}} Final class TraversalRunnable {@override public voidrun() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    void doTraversal() {
        if(mTraversalScheduled) {··· // performTraversals(); ...}}Copy the code

The View tree is drawn from the performTraversals() method of the ViewRootImpl class, which controls the traversals. Each View control is responsible for drawing itself, and the ViewGroup is responsible for telling its child views to draw.

Next, let’s use performTraversals() as the starting point to analyze the entire drawing process of the View.

3. Drawing process of View

ViewRootImpl.java

    private void performTraversals() { final View host = mView; Int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... performLayout (lp, mWidth, mHeight); ... performDraw (); ...}Copy the code
  • PerformMeasure () : The purpose of this stage is to figure out how big each control in the View tree needs to be to display its contents.
  • PerformLayout () : The basic idea of this phase is to start at the root View and recursively complete the layout of the entire View tree.
  • PerformDraw () : This stage also traverses the View tree from the root node down, completes the drawing of all viewgroups and views, and draws the current content of all views to the screen according to the display area calculated during the layout process.

OnMeasure () onLayout() onDraw() onMeasure() onLayout() onDraw() onMeasure() These methods correspond to the three performXXX() methods in the source code of the ViewRootImpl class above. Let’s look at each of them.

3.1 performMeasure

ViewRootImpl.java

private void performMeasure(int childWidthMeasureSpec, Int childHeightMeasureSpec) {··· try {mview.measure (childWidthMeasureSpec, childHeightMeasureSpec); } finally {···}}Copy the code

In addition, we can see that measure() is final, so we can’t override it ina subclass. Android does not allow us to change the measure framework of View.

Let’s look at the measure() method of View:

View.java

Public final void measure(int widthMeasureSpec, int heightMeasureSpec)if(forceLayout | | needsLayout) {... int cacheIndex = forceLayout? -1 : mMeasureCache.indexOfKey(key);if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should setthe measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); ...}else{···} ···}Copy the code

The Measure phase is the most complex, so here we cut it short and cut out the MeasureSpec analysis. The measure() method is final, so the View subclass can only implement its own measure logic by overloading onMeasure(). And then the actual measurement is done to see if the conditions for redrawing are met, forceLayout, This can be done via view.requestLayout ()) or needsLayout (which means that the MeasureSpec passed this time is different from the last one passed) is true.

View.java

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

Here’s a quick summary:

  • Measure the View and its contents to determine the width and height of the measurement. The method isMeasure (int, int)Call, and should be overridden by subclasses to provide accurate and efficient measurements of their contents.
  • When overriding this method, you must callsetMeasuredDimension(int,int)To store the width and height measured by the view. Failure to do so will trigger an IllegalStateException, set tomeasure(int,int)Sell them.
  • The measured base class implementation defaults to the dimensions of the background, unless MeasureSpec allows a larger size. Subclasses should overrideonMeasure(int,int)Method to provide better measurements of content.
  • If the method is overridden, the subclass is responsible for ensuring that the measured height and width are at least the minimum height and width values for the View.
  • For non-viewgroup views, this is done by calling the above defaultonMeasure()The View measurement can be completed(You can also reload, of courseonMeasure()And call thesetMeasuredDimension()To set the layout to any size, this can be decided according to actual requirements, that is, if you don’t want to use the system’s default measurement, you can customize it as you wish).
  • The mMeasuredWidth and mMeasuredHeight members of the View will not have specific values until the setMeasuredDimension() method is used to set the measure of the View. After the setMeasuredDimension() method is called, we can use getMeasuredWidth() and getMeasuredHeight() to get the measured width and height of the view, both of which are 0.
  • A layout usually contains multiple sub-views, each of which needs to be measured once. One is defined in ViewGroupmeasureChildren()measureChild(),measureChildWithMargins()Method to measure the size of a subview, and all three methods end up calling the subviewmeasure()Methods.measureChildren()The inside is essentially a circular callmeasureChild()And themeasureChild()measureChildWithMargins()The difference is whether margin and padding are also used as the size of the subview. (See source code below)

ViewGroup.java

    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); } } } 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); } protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }Copy the code

3.2 performLayout

When the measure process is completed, the next stage will be the layout stage, namely the layout stage. The function of layout is to determine the position of the View according to the size measured before and other property values set.

ViewRootImpl.java

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, Int desiredWindowHeight) {··· final View host = mView; ··· Try {host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); ...if(numViewsRequestingLayout > 0)if(validLayoutRequesters ! = null) {··· host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); ···}} finally {···} ··}Copy the code

The DecorView inherits FrameLayout. Let’s take a look at the layout() method in the ViewGroup:

ViewGroup.java

    @Override
    public final void layout(int l, int t, int r, int b) {
        if(! mSuppressLayout && (mTransition == null || ! MTransition. IsChangingLayout ())) {... super. The layout (l, t, r, b); }else{...}}Copy the code

As you can see, just like measure(), the layout() method of the ViewGroup is final. Android does not allow custom subclasses of the ViewGroup to change the layout framework of the ViewGroup. This calls the View’s layout() method directly. Let’s take a look at the View’s layout() method.

View.java

Public void layout(int l, int t, int r, int b) {··· Boolean = 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
  • layout()Method will callsetFrame()Method,setFrame()The method is to actually perform the steps of the layout task, as far assetOpticalFrame()Method, which is also calledsetFrame()Method to perform the layout by setting the mLeft, mTop, mRight, and mBottom parameters of the View, which describe the position of the View relative to its parent View.
  • insetFrame()Method to determine if the position of the View has changed to determine if it is necessary to redraw the current View.
  • And the part of the child View is passedonLayout()Method, since non-viewGroup views do not contain subviews, so the View classonLayout()The method is empty. Because the layout process is the process of the parent layout container laying out the child views,onLayout()Method has no meaning for leaf View, only ViewGroup is useful.

Next let’s look at the onLayout() method of the ViewGroup:

ViewGroup.java

    @Override
    protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);
Copy the code

As you can see, the onLayout() method in ViewGroup is an abstract method, which means that all subclasses of ViewGroup (FrameLayout, LinearLayout, RelativeLayout) must override this method. The subviews are then laid out internally according to their own rules. DecerView inherits from FrameLayout. Let’s look directly at FrameLayout’s onLayout() method.

FrameLayout.java

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        layoutChildren(left, top, right, bottom, false/* no force left gravity */); } void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) { final int count = getChildCount(); ...for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if(child.getVisibility() ! Final int measuredWidth = child-measuredWidth (); final int height = child.getMeasuredHeight(); ·· Child. layout(childLeft, childTop, childLeft + width, childTop + height); }}}Copy the code
  • As you can see, it’s also called on each of the subviewslayout()The method. If the subview is still the parent, the recursion continues; If it’s a leaf view, it’s going to go to the viewonLayout()Empty method, the leaf view layout flow is finished.
  • Width and height respectively come from the measurement value stored in the measure stage. If width and height values are assigned through other channels, the measurement in the measure stage is meaningless.
  • inonLayout()When the procedure is over, we can callgetWidth()Methods andgetHeight()Method to get the width and height of the view.
  • getWidth()Methods andgetMeasureWidth()Differences in methods:getMeasureWidth()Methods in themeasure()The value can be obtained after the phase is over, andgetWidth()Methods tolayout()You can only get it after the phase is over. In addition,getMeasureWidth()The value in the method is passedsetMeasuredDimension()Method, andgetWidth()The values in the method are computed by subtracting the coordinates on the left from the coordinates on the right of the view.

Inside the custom View, If in onLayout () method for a child view layout () method is introduced into four parameters is 0, 0, childView. GetMeasuredWidth () and childView. GetMeasuredHeight (), Then getWidth() and getMeasuredWidth() will give the same value; If the four parameters passed in are other custom values, the getWidth() method and getMeasuredWidth() method will not return the same values (this is not recommended).

Here, we have finished the analysis of the general flow of the layout stage. This stage is mainly to determine the final display position of the View according to the measured width and height of the View obtained in the previous stage.

3.3 performDraw

ViewRootImpl.java

    private void performDraw() {··· try {draw(fullRedrawNeeded); } private void draw(Boolean fullRedrawNeeded) {···if(! dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {if(mAttachInfo.mThreadedRenderer ! = null && mAttachInfo. MThreadedRenderer. IsEnabled ()) {...}else{...if(! drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {return; ···} private Boolean drawSoftware(Surface Surface, AttachInfo, int xoff, int yoff, Boolean scalingRequired, Rect dirty) {··· ·· try {··· try {··· mview.draw (canvas); Finally {···}} Finally {···} ···}Copy the code

If we trace the code, we’ll see that we start with performDraw() and end with mview.draw (canvas); This mView is the DecorView we analyzed earlier:

DecorView.java

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        if (mMenuBackground != null) {
            mMenuBackground.draw(canvas);
        }
    }
Copy the code

Here, the DecorView directly calls the parent class’s draw() method, and then draws the menu background. Let’s focus on the parent class’s method. Neither FrameLayout nor ViewGroup overwrite the draw() method.

View.java

@callSuper public void draw(Canvas Canvas) {·· /* * draw traversal Steps Several drawing stepswhich must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if(! dirtyOpaque) { drawBackground(canvas); } // skip step 2 & 5if possible (common case) final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) ! = 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) ! = 0;if(! verticalEdges && ! horizontalEdges) { // Step 3, draw the contentif(! dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); drawAutofilledHighlight(canvas); // Overlay is part of the content and draws beneath Foregroundif(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

In the Draw phase, the comments in the source code are also clear and clear, here a total of 7 steps are executed, of which step 2 and step 5 are generally ignored, here also simplify the complexity, only analysis of the other 5 steps.

Step 1. Draw the background; Step 2. Ignore and skip; Step 3. Draw content; Step 4. Draw subviews; Step 5. Ignore and skip; Step 6. Draw decorations (foreground, scroll bar); Step 7. Draw the default focus highlight.

Here we focus on Step 3 and Step 4. Step 3: The familiar onDraw() method is called:

View.java

    /**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    protected void onDraw(Canvas canvas) {
    }
Copy the code

Nothing is done in this method, but the comments make it clear that you should override this method to complete the drawing you want. Because the content portion of each View is different, it is up to subclasses to implement the specific logic. Take the DecorView for example, where neither ViewGroup nor FrameLayout overridden the onDraw() method, only the DecorView overridden it. DecorView overwrites onDraw() to implement its own drawing.

DecorView.java

    @Override
    public void onDraw(Canvas c) {
        super.onDraw(c);

        // When we are resizing, we need the fallback background to cover the area where we have our
        // system bar background views as the navigation bar will be hidden during resizing.
        mBackgroundFallback.draw(isResizing() ? this : mContentRoot, mContentRoot, c,
                mWindow.mContentParent);
    }

Copy the code

After mapping your requirements, go to dispatchDraw() in Step 4:

View.java

    /**
     * Called by draw to draw the child views. This may be overridden
     * by derived classes to gain control just before its children are drawn
     * (but after its own view has been drawn).
     * @param canvas the canvas on which to draw the view
     */
    protected void dispatchDraw(Canvas canvas) {

    }
Copy the code

The View’s dispatchDraw() method is an empty method, but the comment says that if the View contains subclasses that need to be overwritten, it actually doesn’t make sense for a leaf View because it doesn’t have any more subviews to draw, and for viewgroups, You need to override this method to draw its subviews. But we can see that familiar layouts like RelativeLayout, LinearLayout, DecorView, and so on don’t override the dispatchDraw() method, so let’s look directly at the ViewGroup:

ViewGroup.java

@override protected void dispatchDraw(Canvas)for(int i = 0; i < childrenCount; I++) {...if((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() ! = null) { more |= drawChild(canvas, transientChild, drawingTime); }...if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        while(transientIndex > = 0) {/ / there may be additional transient views after the normal views... more | = drawChild (canvas, transientChild, drawingTime); ...}... / / the Draw any disappearing views that have animationsif(mDisappearingChildren ! = null) {...for(int i = disappearingCount; i >= 0; I -) {... more | = drawChild (canvas, child, drawingTime); ...}}}Copy the code

DrawChild () drawChild() drawChild() drawChild() drawChild() drawChild() drawChild() drawChild() drawChild() drawChild() drawChild() drawChild() drawChild()

    /**
     * Draw one child of this View Group. This method is responsible for getting
     * the canvas in the right state. This includes clipping, translating so
     * that the child's scrolled origin is at 0, 0, and applying any animation * transformations. * * @param canvas The canvas on which to draw the child * @param child Who  to draw * @param drawingTime The time at which draw is occurring * @return True if an invalidate() was issued */ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this, drawingTime); }Copy the code

The drawChild() method simply calls the draw() method of the child View. Similarly, if the subview has subviews, the drawChild() method will continue to draw the subview until it reaches the leaf View, and so on and so on until the tree is complete.

  • From the analysis above, we know that the View does not draw the content for us, so we need each View to draw its own content based on what it wants to show. That’s what we rewrite when we’re customizing the ViewonDraw()Methods.
  • If the View is a ViewGroup, you need to recursively draw all the child views it contains.
  • The main way to draw is through the Canvas class, which is passed in as a parameteronDraw()Method for each view to use. The Canvas class can be used in a variety of ways. You can basically use it as a Canvas and draw anything on it.
  • Get the canvas clipping area (for each View)draw()Method), the child View gets the Canvas without paying attention to this logic. All it cares about is drawing the Canvas.
  • The default is subviewViewGroup.drawChild()The drawing order is the same as the order in which the child views were added, but you can also reloadViewGroup.getChildDrawingOrder()Method provides a different order.
  • At this point, from the Activity instance creation, we have analyzed the entire source call process, to the actual drawing process of the View.
  • Take the Activity DecorView as an example, the whole View system is a View tree with the root of the DecorView, and the process of measure, layout and draw is completed by traversal in turn.
  • In a custom View, the drawing process of each View must go through three main stages, namely, rewritingonMeasure().onLayout().onDraw()To complete the parts you want to customize.