This is the 17th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

MeasureSpec

MeasureSpec is a class that is composed of MODE_MASK and MODE_SHIFT a 32-bit int value, the former two code SpecMode, 30 represents SpecSize behind. SpecMode indicates the measurement mode; SpecSize indicates the size of the specification in a certain measurement mode.

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 {}
   
    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); }}@UnsupportedAppUsage
    public static int makeSafeMeasureSpec(int size, int mode) {
        if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
            return 0;
        }
        returnmakeMeasureSpec(size, mode); }}Copy the code

SpecMode

The SpecMode modes are UNSPECIFIED, EXACTLY, and AT_MOST. All three modes are obtained by bit operation.

  • UNSPECIFIED

UNSPECIFIED indicates that the parent container has no restrictions on the View, and the View can set its own size. Does not need the developer to care, by the system internal call. For example, the ScrollView view.

  • EXACTLY

EXACTLY means that the parent container has measured the required size of the View. MeasureSpec SpecSize corresponds to LayoutParams’ match_parent and the specified value.

  • AT_MOST

AT_MOST indicates that the parent container tops the available size SpecSize. The size of the View is within this value range. SpecSize in MeasureSpec corresponds to WRAP_content in LayoutParams.

Relationship between LayoutParams and MeasureSpec

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec A MeasureSpec is determined by LayoutParams and the parent container.

A DecorView’s MeasureSpec is calculated slightly differently from a normal View’s. A DecorView’s MeasureSpec is determined by the screen size and LayoutParams. Its default LayoutParams is match_parent. For a normal View, MeasureSpec is determined by the parent container’s MeasureSpec and its own LayoutParams.

MeasureSpec create

LayoutParams influence

In ViewRootImpl, getRootMeasureSpec is executed in the measureHierarchy method, which evaluates the View’s MeasureSpec. The MeasureSpec creation logic is clear from getRootMeasureSpec.

###ViewRootImpl.measureHierarchy 
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {... childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); . } ###ViewRootImpl.getRootMeasureSpec
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

Create different MeasureSpec’s based on the View’s LayoutParams property in the getRootMeasureSpec method.

  • LayoutParams.MATCH_PARENT

Exact mode, size is the size of the parent container window.

  • LayoutParams.WRAP_CONTENT

The size is indeterminate but does not exceed the maximum size of the parent container window.

  • default

Default is not set, the precise mode size is determined by the set size.

The parent container ViewGroup is affected

On the ViewGroup, there is a method measureChildWithMargins that calculates the size of the child View. The child View’s MeasureSpec is calculated using getChildMeasureSpec before calling child.measure(). The parent container affects the size calculation of the child View.

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 ViewGroup’s getChildMeasureSpec method calculates the child View’s SpecMode by obtaining the parent’s SpecMode and then calculating the child View’s SpecMode through the child View’s LayoutParams. Finally through the MeasureSpec. MakeMeasureSpec (resultSize resultMode) method of the View MeasureSpec.

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:
       ....../ / same as above
    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        ....../ / same as above
        break;
    }
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code
Child View Size/ parent SpecMode EXACTLY AT_MOST UNSPECIFIED
Specific size setting EXACTLY EXACTLY EXACTLY
match_parent EXACTLY AT_MOST UNSPECIFIED
wrap_content AT_MOST AT_MOST UNSPECIFIED
  • When a child View is set to a specific size, whatever the parent SpecMode is, the child View’s SpecMode is EXACTLY the same.
  • The child View size is match_parent, and the child View’s SpecMode is the parent’s SpecMode. The size is the maximum remaining size of the parent container.
  • The size of the child View is WRap_content and the SpecMode of the child View is AT_MOST. UNSPECIFIED only if the parent SpecMode is UNSPECIFIED and the child View has LayoutParams set.