4 View drawing process third step: recursive draw source code analysis

Mview.layout will be executed when measure and layout are executed in view otimpl performTraversals.

private void performTraversals() {... final Rect dirty = mDirty; . canvas = mSurface.lockCanvas(dirty); . mView.draw(canvas); . }Copy the code

The draw process is also traversals inside ViewRootImpl’s performTraversals() after measure() and layout(), where mView is phoneWindow.decorView for Actiity, The code in ViewRootImpl creates a Canvas object and calls the View’s Draw () method to do the actual drawing. So it’s back to the tree-like recursive draw process of ViewGroup and View.

First look at the View tree recursive draw flow chart, as follows:

We examine this process in detail below.

4-1 DRAW source code analysis

Draw (ViewGroup); draw (ViewGroup); draw (ViewGroup); draw (ViewGroup);

   public void draw(Canvas canvas) {
        ......
        /*
         * Draw traversal performs several drawing steps which 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
        ......
        if(! dirtyOpaque) { drawBackground(canvas); } // skip step 2 & 5if possible (common case)... // Step 2, save the canvas' layers ...... if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); }... // Step 3, draw the content if (! dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers ...... if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); p.setShader(fade); canvas.drawRect(left, top, right, top + length, p); }... // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); . }Copy the code

See the whole View’s draw method is complicated, but the source code comments are also obvious. You can see from the comments that the whole DRAW process is divided into six steps. The source notes say (” skip steps 2 & 5 if possible (common Case) “) that steps 2 and 5 can be skipped, so we will focus on the remaining four steps. As follows:

The first step is to draw the View’s background.

As you can see, the draw method tranches drawBackground(canvas); Method to realize background rendering. Let’s take a look at the source of this method, as follows:

Private void drawBackground(Canvas Canvas) {// Get XML from android:background property or codesetBackgroundColor (),setDrawable final Drawable background = mBackground; Drawable final Drawable background = mBackground; . // Set the drawing area of the background according to the View position determined by the Layout procedureif (mBackgroundSizeChanged) {
            background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
            mBackgroundSizeChanged = false;
            rebuildOutline();
        }
        ......
            //调用Drawable的draw()方法来完成背景的绘制工作
            background.draw(canvas);
        ......
    }
Copy the code

Third, draw the contents of the View.

View onDraw() : View onDraw() : View onDraw() : View onDraw() : View onDraw() : View onDraw()

 /**
     * 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

As you can see, this is an empty method. Because the content portion of each View is different, it is up to the subclasses to implement the concrete logic.

The fourth step, to the current View of all the child View to draw, if the current View does not have child View do not need to draw.

Let’s look at dispatchDraw(canvas) in View’s draw method; Method source, can be seen as follows:

  /**
     * 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

See, the View dispatchDraw() method is null, and the comment says that if the View contains subclasses it needs to be overridden, so we need to look at the ViewGroup dispatchDraw method source code. If the View does not have any children, the method is empty, and the ViewGroup is implemented), as follows:

@Override protected void dispatchDraw(Canvas canvas) { ...... final int childrenCount = mChildrenCount; final View[] children = mChildren; .for (int i = 0; i < childrenCount; i++) {
            ......
            if((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() ! = null) { more |= drawChild(canvas, child, drawingTime); }}... // Draw any disappearing views that have animationsif(mDisappearingChildren ! = null) { ......for(int i = disappearingCount; i >= 0; i--) { ...... more |= drawChild(canvas, child, drawingTime); }}... }Copy the code

As you can see, the ViewGroup does override the View’s dispatchDraw() method, which internally iterates through each child View and calls drawChild(). We can look at the ViewGroup’s drawChild method as follows:

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

You can see that the drawChild() method invokes the draw() method of the child View. So the ViewGroup class has already overwritten the dispatchDraw() implementation for us. We generally don’t need to override this method, but we can override the parent class function to do something specific.

Step 6: Draw the View’s scroll bar.

So we’re going to call the View onDrawScrollBars() method, so let’s look at the View onDrawScrollBars(canvas); The methods are as follows:

/**
     * <p>Request the drawing of the horizontal and the vertical scrollbar. The
     * scrollbars are painted only if they have been awakened first.</p>
     *
     * @param canvas the canvas on which to draw the scrollbars
     *
     * @see #awakenScrollBars(int)*/ protected final void onDrawScrollBars(Canvas Canvas) {// DrawScrollBars }Copy the code

As you can see, every View has a scroll bar, but it’s not normally displayed.

To this point, View draw draw part of the source code analysis is completed, we next carry out some summary.

4-2 Summary of DRAW principle

As you can see, the drawing process is to draw the View object onto the screen. The whole draw process needs to pay attention to the following details:

  • If the View is a ViewGroup, you need to recursively draw all the child views it contains.

  • A View does not draw anything by default; the actual drawing is done in a subclass.

  • The View is drawn using the Canvas class passed in by the onDraw method.

  • Distinguish between View animation, which refers to the View’s own animation and can be added by setAnimation, and ViewGroup layout animation, which is set specifically for the ViewGroup to display its internal child views. You can set the ViewGroup to a layoutAnimation property in an XML layout file.

  • The padding is automatically removed when fetching the Canvas clipping area (the Canvas passed in each View’s draw). The child View fetching the Canvas does not have to worry about this logic, just how to draw it.

  • Default following the View of ViewGroup. DrawChild drawing order and the View is added order, but you can overload ViewGroup. GetChildDrawingOrder () method provides a different order.



                       

Follow “DvlpNews”

Grasp the pulse of cutting-edge technology