ViewRoot and DecorView

The ViewRoot corresponds to the ViewRootImpl class, which is the link between The Windows Manager and the DecorView. The View’s three main processes are done through ViewRoot. In ActivityThread, when the Activity object is created, The DecorView is added to the Window, the ViewRootImpl object is created, and the ViewRootImpl is associated with the DecorView

A DecorView, as a top-level View, typically contains a vertical LinearLayout with a header bar and a interior bar. In the Activity, the layout of the setContentView is added to the content bar. The id of the content bar is Content. How to get the content? FindViewById (Android.r.icontent)), how to get the View we set in our Activity, content.getChildAt(0); Also, the DecorView is actually a FrameLayout, and all View events are passed to our View through the DecorView

View’s three main drawing processes

The rendering process of View starts from the Display traversals method of View wrootimpl. A View can be drawn only after measure, layout and draw, in which measure measures the width and height of the View. Layout is used to determine the position of the View in the parent container, and Draw is responsible for drawing the View on the screen, as shown below:

The performMeasure method calls the measure method, and the onMeasure method calls the onMeasure method. In the onMeasure method, the measure process is carried out on all the child elements. At this time, the measure process is transferred from the parent element to the child element. This completes a measure process, and then the child element repeats the parent element’s measure process, which completes the traversal of the View tree. Similarly, performLayout and performDraw transfer processes are similar to performMeasure. The only difference is that the preformDraw transfer process is implemented in the DRAW method via dispatchDraw, essentially the same

  • The measure procedure determines the width and height of the View. Once the measure is complete, the measuredWidth and measuredHeight can be obtained
  • The layout process determines the four vertices of the View and the actual width and height of the View. After completion, getTop, getLeft, getRight, getBottom can obtain the four vertices of the View, and getWidth and getHeight can obtain the final width and height of the View
  • The draw process determines the display of the View, and only after the draw method is complete will the View’s contents appear on the screen

Understand the MeasureSpec

MeasureSpec participates in the measure process of a View. MeasureSpec largely determines the size of the View, largely because it is also affected by its parent, which influences the creation of a View’s MeasureSpec

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = 32 bits; Let’s look at some definitions of MeasureSpec’s internal constants

public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT)1)int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return(size & ~MODE_MASK) | (mode & MODE_MASK); }}@MeasureSpecMode
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

 
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
Copy the code

MeasureSpec avoids excessive object memory allocation by packaging SpecMode and SpecSize into an int. For ease of operation, I can package and unpackage them. SpecSize and SpecMode are also int values. A SpecMode and a SpecSize can be packaged into a MeasureSpec, and a MeasureSpec can be unpackaged into a SpecSize and a SpecMode

There are three types of specmodes, each of which represents a special meaning, as shown below

UNSPECIFIED

The parent container does not have any restrictions on the View and can be as large as it wants. This situation generally applies to the internal system, indicating a measurement state

EXACTLY

The parent container has already detected the exact size required by the View. At this point, the final size of the View is the value of SpecSize, which corresponds to both mach_paraent and concrete value modes in LayoutParams

AT_MOST

The parent container specifies an available size: SpecSize. The size of the View cannot be larger than this value, which depends on the implementation of the View

MeasureSpec and LayoutParams

We give the View set LayoutParams, when the View measurement, the system will set the LayoutParams and the parent container under the constraints of conversion cost View MeasureSpec, and then according to this MeasureSpec determine the width and height of the View measurement

Also, a MeasureSpec conversion is different for a top-level View (i.e., a DecorView) and a normal View,

  • For a DecorView, its MeasureSpec is determined by the size of the window and its own LayoutParams
  • For a normal View, its Measurespec is determined by the parent container’s Measurespec and its own LayoutParams
  • MeasureSpec Once a MeasureSpec is determined, the onMeasure can determine the width and height of the View

For the DecorView, the measureHierarchy method in ViewRootImpl contains the following code that shows how the DecorView creates MeasureSpec

 			childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
Copy the code

Where desiredWindowWidth and desiredWindowHeight refers to the screen size, then look at the getroot Measure method source code

     int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }
Copy the code

From the code above, it is clear how the DecorView and MeasureSpec are generated, divided by the width and height parameters in his LayoutParams

  • Layoutparams.match_parent: Exact mode where the size is the size of the window
  • Layoutparams.wrap_content: Maximum mode, size indeterminate, but no larger than the window size
  • Fixed size: Exact mode with the size specified in LayoutParams

For ordinary views, the measure process is passed by the ViewGroup. See measureChildWithMargins method for a ViewGroup

   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

The measure method of the child element is called by getChildMeasureSpec. The measure method of the child element is called by getChildMeasureSpec. The parent’s Measure, the LayoutParams, and the margin and padding of the View. See the getChildMeasureSpec method on the ViewGroup

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
Copy the code

The parent container’s MeasureSpec and the View’s LayoutParams are the parent container’s MeasureSpec. The padding is the size of the parent container’s size. So the available size of the child element is the size of the parent element minus the padding, as follows:

int size = Math.max(0, specSize - padding);
Copy the code

The method getChildMeasureSpec clearly shows the rules for creating a common View’s MeasureSpec. In order to see the logic of getChildMeasureSpec more clearly, we provide a table for the method getChildMeasureSpec

  • When the subview is fixed in width and height
    • Whatever the parent container’s MeasureSpec is, the View’s MeasureSpec is exact and follows the size in LayoutParams, right
  • When the width of the child View is mach_parent
    • The parent container is the exact mode, the child View is the exact mode, and the size is the remaining space of the parent container
    • The parent container is the largest mode, and the child View is also the largest mode, and is not larger than the remaining space of the parent container
  • The width and height of the child View is warp_content
    • Regardless of whether the parent container is precise or Max mode, the mode of the View is always Max mode and cannot exceed the remaining size of the parent container

The UNSPECIFIED mode is not discussed here, as it is mainly used for multiple measures within the system and generally does not require attention

View workflow

The workflow of View mainly refers to measure, layout and draw, namely, measurement layout and drawing. Measure determines the measurement width and height of View, Layout determines the final width and height of View and the positions of four vertices, while DRAW draws the View on the screen

Measure the process

There are two cases of measure process. If it is an original View, its measurement is completed by measure method. If it is a ViewGoup, it not only completes its own measurement, but also traverses and calls the measure methods of all its child elements, and then each child element performs this process. The following is an analysis of these two processes

A View’s measure process

The measure process of View is completed by measure method, which is a final type method and cannot be subclass rewritten. The onMeasure method of View will be called in the measure method of View. We just need to see the implementation of onMeasure method

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

The setMeasuredDimension method sets the View width and height measurements. Let’s look at getDefaultSize

  public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
Copy the code

= MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec The SpecSize is the measured size of the View. The measured size is mentioned several times here, because the final size of the View is determined in the layout stage, so it is distinguished here

UNSPECIFIED is typically used for measurement within the system. In this case, the size of the View is the first parameter of getDefaultSize, High getSuggestedMinimumWidth respectively and wide in getSuggestedMinimumHeight return values of the two methods, have a look at their source

 protected int getSuggestedMinimumWidth(a) {
        return (mBackground == null)? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }protected int getSuggestedMinimumHeight(a) {
        return (mBackground == null)? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight()); }Copy the code

Suggestedminimumwidth is just implemented from the getSuggestedMinimumWidth implementation, and you can see from the code that if the View had no background set, the width of the View would have been mMinWidth, and mMinWidth stands for the value specified by the Android :minWidth attribute. If you do not specify this attribute is the default is 0, if the View is set to the background, the width of the View as Max (mMinWidth, mBackground getMinimumWidth ()), Then we study the mBackground. GetMinimumWidth () is what

  public int getMinimumHeight(a) {
        final int intrinsicHeight = getIntrinsicHeight();
        return intrinsicHeight > 0 ? intrinsicHeight : 0;
    }
Copy the code

The getMinimumHeight method returns the original width of the Drawadle if there is a original width, ShapeDrawable does not have a original width, and BitmapDrawable does

Let’s summarize the logic of getSuggestedMinimumWidth

  • Returns if the View has no background setandroid:minWidthThe value of this attribute, if not specified, defaults to 0
  • Returns if the View has a background setandroid:minWidthThe value of this property and the maximum background minimum width
  • GetSuggestedMinimumWidth returns the measured width and height in the case of UNSPECIFIED

From the perspective of getDefaultSize, the width and height of View is determined by SpecSize. We can draw the following conclusion: Directly inherit the custom control of View, we need to rewrite the onMeasure method and set the size of wrAP_content. Otherwise using wrap_content is equivalent to using mach_parent. Why?

A child View’s Child size is wrap_content. A child View’s child size is wrap_content. A child View’s child size is wrap_content. The child View’s SpecMode is AT_MOST, regardless of the parent’s child size. In this case, the View’s SpecSize is the size of the parent’s child size.

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int width = 80;
        int height = 80;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
        int heigthSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.AT_MOST && heigthMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(width, height);
        } else if (widthMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(width, heigthSize);
        } else if(heigthMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSize, height); }}Copy the code

We specify a default width and height for the View. When setting warp_Content, we directly set this width and height. For non-WARp_Content, we need to use the system measurement value. TextView and other controls take special care of the WARp_Content case

Measure procedure of VIewGroup

For ViewGroup, in addition to executing its own measure process, it also needs to traverse all its child measure methods, and each child element performs this process recursively. Different from View, ViewGroup does not rewrite the onMeasure method of View. But it does provide a measureChildren method, as shown in the source code below:

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

The child’s Measure method is used to measure the child’s MeasureSpec

There is no specific measurement process for ViewGroup, because ViewGroup is an abstract class and its onMeasure method needs to be realized by all subclasses. Why can’t it be realized uniformly? Because each subclass of a ViewGroup has different layout properties, the measurement process is different. For example, LineraLayout and RelativeLayout have different layout properties. Now let’s analyze the onMeasure implementation of LineraLayout

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

The code above is simple, and let’s examine measureVertical in the vertical case

   // See how tall everyone is. Also remember max width.
        for (int i = 0; i < count; ++i) {
            finalView child = getVirtualChildAt(i); . measureChildBeforeLayout(child, i, widthMeasureSpec,0,
                        heightMeasureSpec, usedHeight);

                final int childHeight = child.getMeasuredHeight();
             
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                       lp.bottomMargin + getNextLocationOffset(child));
Copy the code

MeasureChildBeforeLayout (measureChildBeforeLayout, measureChildBeforeLayout, measureChildBeforeLayout, measureChildBeforeLayout, measureChildBeforeLayout, measureChildBeforeLayout, measureChildBeforeLayout) The system will use mTotalLength to record the height of LineraLayout in the vertical direction, each measurement of a child element, mTotalLength will increase, when the child element is finished, LineraLayout will measure its own size, source code is

 		// Add in our padding
        mTotalLength += mPaddingTop + mPaddingBottom;

        int heightSize = mTotalLength;

        // Check against our minimum height
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

        // Reconcile our calculated size with the heightMeasureSpec
        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
        
		maxWidth += mPaddingLeft + mPaddingRight;

        // Check against our minimum width
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);
Copy the code

LineraLayout will measure its size according to the size of the child element. LineraLayout will measure its size according to the size of the child element. In the horizontal direction, LineraLayout will follow the measurement process of View, but in the vertical direction, the measurement is different. If the layout is warp_content, then the height is the sum of the height occupied by all the child elements, but it still cannot exceed the remaining space of the parent element. Of course, there is also the padding, check the source code

 public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);
        final int result;
        switch (specMode) {
            case MeasureSpec.AT_MOST:
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
            default:
                result = size;
        }
        return result | (childMeasuredState & MEASURED_STATE_MASK);
    }
Copy the code

The measure process of View is the most complex of the three processes. After measur is completed, the measurement width and height can be obtained. It should be noted that in some extreme cases, measure is required for several times to determine the measurement width and height

The Layout process

Layout determines the position of the four vertices and the final width and height of the View. If it is a ViewGroup, when the position of the ViewGroup is determined, it will traverse the child elements in onLayout and call the layout method of the child elements. Within the Layout method, onLayout will be called again and so on. The Layout method determines the position of the View itself, while the onLayout method determines the position of all the child elements. Take a look at the View layout method

  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);

            if (shouldDrawRoundScrollbar()) {
                if(mRoundScrollbarRenderer == null) {
                    mRoundScrollbarRenderer = new RoundScrollbarRenderer(this); }}else {
                mRoundScrollbarRenderer = null;
            }

            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
            }

Copy the code

The general layout process is as follows. First, the setFrame method will be used to set the values of the four vertices of the View and initialize the values of mLeft, mTop, mRight and mBottom. Once the four vertices of the View are determined, the position of the View in the parent container will be determined. The onLayout method is used to determine the location of the child elements in the parent container. The specific implementation of onLayout depends on the specific layout. Neither View nor ViewGroup really implements onLayout

  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

LayoutVertical is selected for analysis

 void layoutVertical(int left, int top, int right, int bottom) {...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();

                finalLinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); .if(hasDividerBeforeChildAt(i)) { childTop += mDividerHeight; } childTop += lp.topMargin; setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child, i); }}}Copy the code

As you can see, this method iterates over the child element and calls setChildFrame to specify the position of the child element. ChildTop gets larger and larger, which means that subsequent child elements are placed lower, which corresponds to LineraLayout’s vertical property. SetChildFrame only calls the layout method of the child element. After the parent element determines its position through layout, it calls the layout method of the child element through onLayout.

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

Note that width and height in setChildFrame are the View’s measured width and height, as shown in the following code

   				final int childWidth = child.getMeasuredWidth();
                final int childHeight = child.getMeasuredHeight();
                setChildFrame(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight);
Copy the code

In layout, the setFrame method is used to set the width and height of the four vertices, as shown below

  			mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
Copy the code

What is the difference between the measured width and final width of a View

If getWidth and getMeasureWidth are different, let’s look at getWidth and getHeight

    public final int getWidth() {
        return mRight - mLeft;
    }

    public final int getHeight() {
        return mBottom - mTop;
    }
Copy the code

According to the above source code, in fact, by default, the measurement width and height of View are the same as the final width and height, but the measurement width and height is formed in the process of measure, while the final width and height is formed in the layout process. The assignment timing is different, because measure is earlier. In the actual development, We can think of the measured width as equal to the actual width, but in some cases they are different, such as:

Rewrite the View layout method

@Override
    public void layout(int l, int t, int r, int b) {
        super.layout(l, t, r+100, b+100);
    }
Copy the code

This will result in the measured width and height being 100px smaller than the actual width and height, which will cause the View to display abnormally and have no practical significance, but also prove that there is an unequal situation

The draw process

The draw process is relatively simple. Its job is to draw a View onto the screen. The draw process follows the following steps

  • DrawBackground (drawBackground(canvas);)
  • Draw yourself (onDraw(canvas);)
  • Draw children (dispatchDraw(canvas))
  • Draw foreground (onDrawForeground(canvas);)
  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;/* * 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
        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

View drawing process is through the dispatchDraw method to the sub-view, dispatchDraw will pass through all the sub-view call sub-view draw method, so draw time is passed down layer by layer, View has a special method setWillNotDraw

  /**
     * If this view doesn't do any drawing on its own, set this flag to
     * allow further optimizations. By default, this flag is not set on
     * View, but could be set on some View subclasses such as ViewGroup.
     *
     * Typically, if you override {@link #onDraw(android.graphics.Canvas)}
     * you should clear this flag.
     *
     * @param willNotDraw whether or not this View draw on its own
     */
    public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }
Copy the code

If a View does not need to draw anything, then setting this flag to true will be optimized accordingly. By default, this flag bit is not enabled for View, but this flag bit is enabled for ViewGroup by default. When our custom control inherits from ViewGroup, it does not have drawing function. You can turn this flag bit on, or turn it off if you want to draw with onDraw