“This is the 14th day of my participation in the First Challenge 2022.

Related articles: DecorView Android View DecorView Android View DecorView View rendering process (3) -Activity View -WindowManager Android View rendering process (4) -Activity View -ViewRootImpl Android View rendering process (5) -Measure Android View View drawing process (six) -Layout Android View drawing process (seven) -Draw

The previous article introduced the main source code for the Activity View, from creating a DecorView to passing it to WindowManager to drawing the ViewRootImpl. This is the overall drawing process. The drawing process is divided into:

  • Measure: Determine whether the View size needs to be recalculated or calculated if necessary.
  • Layout: Determine whether the position of the View needs to be recalculated, if necessary;
  • Draw: Determines whether the View needs to be redrawn or redrawn if necessary.

Today is about measuring Mearsure

Measure

MeasureSpec encapsulates the layout requirements passed from parent to child. MeasureSpec encapsulates the layout requirements passed from parent to child. MeasureSpec encapsulates the layout requirements passed from parent to child. The lower 30 bits represent SpecSize (the exact size of the measurement). Take a look at the source code:

UNSPECIFIED * The parent ViewGroup imposes no constraints on its child views, which can be of any size. This kind of situation is relatively rare, mainly used in the case of multiple measures inside the system. * is generally used in the child view in the container that can be rolled, such as ListView, GridView, RecyclerView in some cases. * In general, we don't need to worry about this pattern. * 2.EXACTLY * The view must use the size specified by the parent ViewGroup. Corresponding to match_parent or a specific value (such as 30dp) * 3.AT_MOST * This View can take the maximum size specified by the parent ViewGroup. The wrAP_CONTENT * * MeasureSpec counterpart uses binary to reduce object allocation. */ public class MeasureSpec {// The size of a MeasureSpec is 32 bits. Private static final int MODE_SHIFT = 30; private static final int MODE_SHIFT = 30; // Operation mask, 0x3 is hexadecimal, 10 is 3, and binary is 11. 3 carries 30 to the left, which is 11 00000000000(11 followed by 30 zeros) // (The function of the mask is to mark the desired value with 1 and the unwanted value with 0. Private static final int MODE_MASK = 0x3 << MODE_SHIFT; // 0 carries 30 bits, which is 00 00000000000(00 is followed by 30 0s) 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; // measureSpec = size + mode; // measureSpec = size + mode; (Note: binary addition, not decimal addition!) // As size=100(4), mode=AT_MOST, then measureSpec=100+10000... 00 = 10000.. 00100 // // second return: // size &; ~MODE_MASK is the last 30 bits of size, mode &amp; MODE_MASK is the number obtained by taking the first two digits of mode and performing the last or operation. The first two digits contain the number representing mode. 30 representative size behind public static int makeMeasureSpec (int size, int mode) {if (sUseBrokenMakeMeasureSpec) {return the size + mode;  } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); // mode = measureSpec &amp; MODE_MASK; // MODE_MASK = 11 00000000000(11 followed by 30 zeros); // MODE_MASK = 11 00000000000(11 followed by 30 zeros); // MODE_MASK = 11 00000000000(11 followed by 30 zeros); // For example 10 00.. 00 00100 & 11.. 00(11 followed by 30 zeros) = 10 00.. Public static int measureSpec (measureSpec) {return (measureSpec & MODE_MASK); // / size = measureSpec & ~MODE_MASK; // MODE_MASK = 00 111111; // MODE_MASK = 00 111111; Size public static int measureSpec (measureSpec) {return (measureSpec & ~MODE_MASK); }}Copy the code

The wrap_content and match_parent values we used in the layout are -2 and -1, respectively.

In the last article we saw that the drawn method calls performTraversals. Take a look at the source code:

//Activity window width and height int desiredWindowWidth; int desiredWindowHeight; . Rect frame = mWinFrame; Rect frame = mWinFrame; If (mFirst) {// If (mFirst) {// If (mFirst) {mFullRedrawNeeded = true; // Whether you need to redetermine Layout mLayoutRequested = true; // There are two cases: // Check whether the window to draw contains the status bar, if yes, remove it. Then determine the height and width of the Decorview to draw if (shouldUseDisplaySize(lp)) {// NOTE -- system code won't try to do compat mode. Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else {// Width and height are values for the entire screen. Configuration config = McOntext.getresources ().getConfiguration(); desiredWindowWidth = dipToPx(config.screenWidthDp); desiredWindowHeight = dipToPx(config.screenHeightDp); }... Else {// This is the case where the length and width of the window have changed, and the changes need to be recorded. // If it is not the first time to use this method, its current width and height are obtained from the previous mWinFrame. desiredWindowHeight = frame.height(); /** * if (desiredWindowWidth! = mWidth || desiredWindowHeight ! = mHeight) { if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame); // Full redraw needed to fit the new window size mFullRedrawNeeded = true; // Need to rearrange the control tree mLayoutRequested = true; // Window window size change windowSizeMayChange = true; }}... // Take a predicate if (layoutRequested){... If (mFirst) {// Whether the view window is currently in touch mode. mAttachInfo.mInTouchMode = ! mAddedTouchMode; // Make sure the Window's touch mode is set to ensureTouchModeLocally(mAddedTouchMode); } else {// Six if statements, determine the changes in the IMAGE value from the last time, change the insetsChanged // the IMAGE value includes some reserved areas of the screen, record some blocked areas and other information if (! mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) { insetsChanged = true; }... // There is a case where we add the layout manually while writing the dialog. When we set the width to Wrap_content, we assign the width and height of the screen, If the width or height of the current window's root layout is specified as WRAP_CONTENT, such as Dialog, then we still give it the maximum width and width. Here is the screen width * / if assigned to it (lp. Width = = ViewGroup. LayoutParams. WRAP_CONTENT | | lp. The height = = ViewGroup. LayoutParams. WRAP_CONTENT)  { windowSizeMayChange = true; // Check whether the window to draw contains the status bar. Then determine the height and width of the Decorview to draw if (shouldUseDisplaySize(lp)) {// NOTE -- system code won't try to do compat mode. Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else { Configuration config = res.getConfiguration(); desiredWindowWidth = dipToPx(config.screenWidthDp); desiredWindowHeight = dipToPx(config.screenHeightDp); } } } } }Copy the code

First, check whether it is the first time to measure the size, if so, check whether there is a status bar, according to the status bar to determine the screen height and display height, second, if it is not the first time, then get from the mWinFrame, and compare with the length, width and height saved previously. If they are not, they need to be remeasured to determine the height.

Once the DecorView size is determined, we call measureHierarchy to measure A MeasureSpec. We don’t need to know how to measure a MeasureSpec, but we can check the source code for MeasureSpec. MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        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

The View’s MeasureSpec is divided by the width and height parameters.

  • MATCH_PARENT: exact mode, the size is the size of the window;
  • WRAP_CONTENT: maximum mode, variable size, but no larger than the window size;
  • Fixed size: Precise mode, size is specified specific width and height, such as 100dp.

Go is the first case for DecorView, DecorView MeasureSpec is established here, can be obtained from MeasureSpec DecorView wide high constraints information. The DecorView’s MeasureSpec is determined, so start fetching the child’s MeasureSpec. When the parent ViewGroup measures the child View, it calls the View class’s measure method, which is final and cannot be overridden. The ViewGroup passes its own widthMeasureSpec and heightMeasureSpec, which indicate some restrictions on the parent View’s width and height, respectively. Especially if the ViewGroup is WRAP_CONTENT, we need to measure the subview first. Only when the width and height of the subview are determined, the ViewGroup can determine how much width and height it needs.

When the DecorView’s MeasureSpec is determined, the ViewRootImpl internally calls the performMeasure method:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}Copy the code

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = Measure ()

/** * Call this method to figure out how big a View should be. Parameter is the constraint information on the width and height of the parent View. Public final void measure(int widthMeasureSpec, int heightMeasureSpec) {...... // Suppress sign extension for the low bytes long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); // If mPrivateFlags contains PFLAG_FORCE_LAYOUT, Forcing a relayout such as view.requestLayout () will add this flag to mPrivateFlags final Boolean forceLayout = (mPrivateFlags &) PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; final boolean specChanged = widthMeasureSpec ! = mOldWidthMeasureSpec || heightMeasureSpec ! = mOldHeightMeasureSpec; final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); final boolean needsLayout = specChanged && (sAlwaysRemeasureExactly || ! isSpecExactly || ! matchesSpecSize); / / need to layout the if (forceLayout | | needsLayout) {/ / first clears the measured dimension flag is marked as not measuring state mPrivateFlags & = ~PFLAG_MEASURED_DIMENSION_SET; / / such as Arabic, Hebrew writing from right to left, the layout of the language of the special processing resolveRtlPropertiesIfNeeded (); Int cacheIndex = forceLayout int cacheIndex = forceLayout int cacheIndex = forceLayout -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); } else {mMeasureCache = mMeasureCache. ValueAt (cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } // If the custom View overrides the onMeasure method, but does not call setMeasuredDimension(), an error will be raised here; // flag not set, setMeasuredDimension() was not invoked, We raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET)! = PFLAG_MEASURED_DIMENSION_SET) {throw new IllegalStateException("View with ID "+ getId() + ": #onMeasure() did not set the" measured dimension by calling" +" SetMeasuredDimension () "); } / / at this point, the View has been measured out and save the results of the measurement in the View of mMeasuredWidth and mMeasuredHeight will mark location to the layout of state mPrivateFlags | = PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; / / save in the cache mMeasureCache. Put (key, ((long) mMeasuredWidth) < < 32 | (long) mMeasuredHeight & 0 XFFFFFFFFL); // suppress sign extension }Copy the code

The above code is mainly the size of the measured View, using Boolean to control whether to measure, whether to read the value from the cache, finally calling onMeasure() method to complete the actual measurement, and save the measurement result value in the cache, using mPrivateFlags value to save the measurement status.

Get the MearsureSpec of the child View:

public static int getChildMeasureSpec(int spec, int padding, Int childDimension) {// Parent view's mode and size int specMode = MeasureSpec. int specSize = MeasureSpec.getSize(spec); // remove the padding int size = math.max (0, specsie-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

MeasureSpec = MeasureSpec; The child View’s MeasureSpec is the parent View’s MeasureSpec and the View’s LayoutParams property LayoutParams have information such as width, height and dimension values. When the width of the child View is fixed, the size of the View is determined by the size of LayoutParams.

When a child View is match and its parent is EXACTLY the size of its parent’s container, the size of a child View is EXACTLY the size of its parent container.

When a child view is wrap, the View’s MeasureSpec is always in the maximized mode and does not exceed the remaining space of the parent container, regardless of whether the parent is in the exact mode or the maximized mode (AT_MOST).

It is shown as follows:

OnMeasuere () : view.onMeasuere () : onMeasuere() : view.onMeasuere () : onMeasuere() : onMeasuere();

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

The above method calls the setMeasuredDimension() method, and setMeasuredDimension() calls getDefaultSize(). GetDefaultSize () and invokes the getSuggestedMinimumWidth () and getSuggestedMinimumHeight ().

GetSuggestedMinimumWidth and getSuggestedMinimumHeight logic is the same, is mainly to obtain the minimum width is high.

Let’s look at the code logic for 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

GetDefaultSize is called by default in the View’s onMeasure(), and wrap_content is identified with match_parent. If a custom View wants to implement wrap, then it needs to rewrite the onMeasure method. Is this familiar? In our development process, the ListView was the first to rewrite the onMeasure method. Fix incomplete listView display.

ResolveSizeAndState () : childState () : childState () : childState ()) : childState () : childState ();

SetMeasuredDimension (resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));Copy the code

If you want to know about resolveSizeAndState, you can refer to the online introduction. The measurement of View is all introduced here, and the layout and draw process of View will be introduced in the next chapter.