Understanding MeasureSpec

MesaureSpec is an inner class of View. It encapsulates the specification size of a View. Including View's width and height information. Its function is that in the process of Measure, the system will convert the LayoutParams of the View into the corresponding MeasureSpec according to the rules imposed by the parent container, and then determine the width and height of the View according to the MeasureSpec in the onMeasure method.

Let's look at the code of the MeasureSpec class:

public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /** @hide */
        @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
        @Retention(RetentionPolicy.SOURCE)
        public @interface MeasureSpecMode {}

        /**
         * Measure specification mode: The parent has not imposed any constraint
         * on the child. It can be whatever size it wants.
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has determined an exact size
         * for the child. The child is going to be given those bounds regardless
         * of how big it wants to be.
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * Measure specification mode: The child can be as large as it wants up
         * to the specified size.
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        /**
         * Creates a measure specification based on the supplied size and mode.
         *
         * The mode must always be one of the following:
         * <ul>
         *  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
         *  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
         *  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
         * </ul>
         *
         * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
         * implementation was such that the order of arguments did not matter
         * and overflow in either value could impact the resulting MeasureSpec.
         * {@link android.widget.RelativeLayout} was affected by this bug.
         * Apps targeting API levels greater than 17 will get the fixed, more strict
         * behavior.</p>
         *
         * @param size the size of the measure specification
         * @param mode the mode of the measure specification
         * @return the measure specification based on size and mode
         */
        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);
            }
        }

        /**
         * Like {@link #makeMeasureSpec(int, int)}, but any spec with a mode of UNSPECIFIED
         * will automatically get a size of 0. Older apps expect this.
         *
         * @hide internal use only for compatibility with system widgets and older apps
         */
        @UnsupportedAppUsage
        public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }

        /**
         * Extracts the mode from the supplied measure specification.
         *
         * @param measureSpec the measure specification to extract the mode from
         * @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
         *         {@link android.view.View.MeasureSpec#AT_MOST} or
         *         {@link android.view.View.MeasureSpec#EXACTLY}
         */
        @MeasureSpecMode
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

        /**
         * Extracts the size from the supplied measure specification.
         *
         * @param measureSpec the measure specification to extract the size from
         * @return the size in pixels defined in the supplied measure specification
         */
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }

        static int adjust(int measureSpec, int delta) {
            final int mode = getMode(measureSpec);
            int size = getSize(measureSpec);
            if (mode == UNSPECIFIED) {
                // No need to adjust size for UNSPECIFIED mode.
                return makeMeasureSpec(size, UNSPECIFIED);
            }
            size += delta;
            if (size < 0) {
                Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
                        ") spec: " + toString(measureSpec) + " delta: " + delta);
                size = 0;
            }
            return makeMeasureSpec(size, mode);
        }

        /**
         * Returns a String representation of the specified measure
         * specification.
         *
         * @param measureSpec the measure specification to convert to a String
         * @return a String with the following format: "MeasureSpec: MODE SIZE"
         */
        public static String toString(int measureSpec) {
            int mode = getMode(measureSpec);
            int size = getSize(measureSpec);

            StringBuilder sb = new StringBuilder("MeasureSpec: ");

            if (mode == UNSPECIFIED)
                sb.append("UNSPECIFIED ");
            else if (mode == EXACTLY)
                sb.append("EXACTLY ");
            else if (mode == AT_MOST)
                sb.append("AT_MOST ");
            else
                sb.append(mode).append(" ");

            sb.append(size);
            return sb.toString();
        }
    }

It can be seen from the constant of MeasureSpec that it represents a 32-bit int value, of which the upper 2 bits represent the specMode, and the lower 30 bits represent the specSize. specMode refers to the measurement mode, and specSize refers to the measurement size.

specMode has 3 modes, as follows:

UNSPECIFIED: The mode is not specified, and the View can be as big as it wants. The parent container is not restricted. It is generally used for internal measurement of the system.

AT_MOST: Maximum mode, corresponding to the wrap_content attribute, the final size of the child View is the specSize value specified by the parent View, and the size of the child View cannot be greater than this value.

EXACTLY: Exact mode. Corresponding to the match_parent attribute and the specific value, the parent container measures the size required by the View, which is the value of specSize.

For each View, a MeasureSpec is held, and the MeasureSpec saves the size specification of the View. In the measurement process of View, save the width and height information through makeMeasureSpec. Get mode and width and height through getMode or getSize. MesaureSpec is affected by its own LayoutParams and the MeasureSpect of the parent container. So for DecorView as the top-level View, it doesn't have a parent container, so how does it get its MeasureSpec? Let's look back at the performTraversals method of ViewRootImpl:

private void performTraversals(){
	...
    if(!mStopped){
		int childWidthMeasureSpec = getRootMeasureSpec(mWidth,lp.width);
		int childHeightMeasureSpec = getRootMeasureSpec(mHeight,lp.height);
		performLayout(lp,desiredWindowWidth,desiredWindowHeight);
    }
}

   if(didLayout) {
	 performLayout(lp,desiredWindowWidth,desiredWindowHeight);
	 ..
   }
   if(!cancelDraw && !newSureface) {
		if(!skipDraw || mReportNextDraw) {
			if(mPendingTransition != null && mPendingTransitions.size > 0) {
				for(int i=0; i< mPendingTransitions.size;++i) {
					mPendingTransitions.get(i).startChangingAimations();
				}
				mPendingTransitions.clear();
			}
			performDraw();
		}
   }
   ...
}

Look at this sentence:

int childWidthMeasureSpec = getRootMeasureSpec(mWidth,lp.width);

Here the getRootMearueSpec method is called. Let's examine what this method does:

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

The first parameter windowSize refers to the size of the window. So for DecorView, its MeasureSpec is determined by its own LayoutParams and the size of the window. This is different from ordinary View. Remember how the normal View is determined? Ordinary View is determined by its own LayoutParams and the MeasureSpec of the parent container. We then look at the following code and we will see that DecorView gets different MeasureSpecs according to its own layoutParams. Therefore, the performMeasure method we mentioned earlier needs to pass in two parameters, namely childWidthMeasureSpec and childHeightMeasureSpec, which represent the width and height of the chid's MesaureSpec. Then go back to the performTraversals method and see what is done inside the perfromMeasure 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);
        }
    }

It can be clearly seen here that the measure method of View is called.

Guess you like

Origin blog.csdn.net/howlaa/article/details/128621322