We all know that in order to determine the size of the View, the system must first obtain MeasureSpec, and then determine the size of the View through MeasureSpec.
MeasureSpec (32 is int) consists of two parts:
SpecMode (high 2 bits): Measurement mode.
SpecSize (low 30 bits): Specification size in a measurement mode.
SpecMode has three categories:
UNSPECIFIED: The parent container does not restrict view size. It is generally used inside the system to represent a measurement state.
EXACTLY: Precise mode. Corresponds to: match_parent and specific value in LayoutPrams.
AT_MOST: Maximum mode. Corresponds to the wrap_content pattern in LayoutParam.
So the question arises. What determines the MeasureSpec? Let's start with the source code.
There is a method called measureChildWithMargins in ViewGroup that measures the size of child view s.
/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param widthUsed Extra space that has been used up by the parent * horizontally (possibly by other children of the parent) * @param parentHeightMeasureSpec The height requirements for this view * @param heightUsed Extra space that has been used up by the parent * vertically (possibly by other children of the parent) */ 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); }
As you can see, in the code, the child Width MeasureSpec and child HeightMeasureSpec are obtained first, and then the child Width MeasureSpec (child HeightMeasureSpec) is measured.
The key is to see how these two MeasureSpecs are obtained through the method getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width); we can see that the acquisition of MeasureSpec is not only related to the LayoutParam of the child element itself, but also to the Maysure Spec of the parent container, and of course, to its padding and margin.
Let's go in and see that this part of the code is a bit long, but the logic is very simple. We analyze it directly in the source code by adding comments.
/** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec The requirements for this view * @param padding The padding of this view for the current dimension and * margins, if applicable * @param childDimension How big the child wants to be in the current * dimension * @return a MeasureSpec integer for the child */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); //Get the measurement mode of the parent container int specSize = MeasureSpec.getSize(spec); //Get the size of the parent container in this measurement mode int size = Math.max(0, specSize - padding); int resultSize = 0; //specSize of child elements int resultMode = 0; //specMode of child elements switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: //If the measurement mode of the parent container is EXACTLY if (childDimension >= 0) { //If the LayoutParam of the child element is a specific value >= 0 resultSize = childDimension; //So the specSize of the child element is child Dimension resultMode = MeasureSpec.EXACTLY; //Then the specMode of the child element is EXACTLY
//The following analysis is the same, so I will not write it. } 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
//Here View. sUseZeroUnspecified MeasureSpec is always false, so resultSize==0; 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; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
From the above analysis, we can see that the acquisition of MeasureSpec of child element is not only related to LayoutParam of child element itself, but also to MaestSpec of parent container, and of course, to its padding and margin.
In fact, we can draw the following conclusions:
parentMeasureSpec
childLayoutParam |
EXACTLY |
AT_MOST |
UNSPECIFIED |
Specific numerical value |
EXACTLLY childSize |
EXACTLY childSize |
EXACTLY childSize |
Match_parent |
EXACTLY parentSize |
AT_MOST parentSize |
UNSPECIFIED 0 |
Wrap_content |
AT_MOST parentSize |
AT_MOST parentSize |
UNSPECIFIED 0 |
parentSize is the remaining space of the parent container.
As can be seen from the table above, when we directly inherit view to implement custom controls, we need to override the onMeasure method and set the size of wrap_content, otherwise using wrap_content is equivalent to using match_parent.
This is the analysis of how MeasureSpec is determined.