Android view measure measurement process analysis
1, Overview
The layout rendering of android View class includes three parts: measurement View, layout View and drawing View
Corresponding measure, layout and draw
Today, let's analyze the process of measure
2, Question
1. What is the overall process
2. Process analysis and interpretation
3. Interpretation of MeasureSpec
4. Example analysis
3, Analysis
1. What is the overall process
The above figure shows the calling process of View, ViewRootImpl and ViewGroup
To sum up: the process of measure is a process of continuous measurement from the root view to the sub view until the last sub view test is completed
2. Process analysis and interpretation
We have learned the whole measurement calling process above, so let's take a detailed look at this process now
The specific drawing process of View is written in my article on the other side (analysis of drawing process of Android View) https://editor.csdn.net/md/?articleId=121084883 )
Let's start with ViewRootImpl's performMeasure (performMeasure is invoked in performTraversals).
1,ViewRootImpl#performMeasure
2013 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); 2014 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 2015 2016 if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth=" 2017 + mWidth + " measuredWidth=" + host.getMeasuredWidth() 2018 + " mHeight=" + mHeight 2019 + " measuredHeight=" + host.getMeasuredHeight() 2020 + " coveredInsetsChanged=" + contentInsetsChanged); 2021 2022 // Ask host how big it wants to be 2023 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
The above method is in performMeasure in ViewRootImpl. The height and width of the screen are calculated, and the sizemode is passed into performMeasure
2,ViewRootImpl#performMeasure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 2273 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); 2274 try { 2275 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 2276 } finally { 2277 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 2278 } 2279 }
This method is very short, that is, it calls mView's measure. As mentioned earlier, mView is the top-level view of window, and decitorview inherits from Framelayout
This measure is a method in View. Let's continue to look at it
3,View#measure()
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int oWidth = insets.left + insets.right; int oHeight = insets.top + insets.bottom; widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); } // Suppress sign extension for the low bytes /* Calculate a measure cached key As can be seen from the code, the key is a long type. The high 32 bits are width and the low 32 bits are height */ long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); //If the layout is forced, the forced layout here refers to the user calling requestLayout, //After requestLayout is called, mPrivateFlags |= PFLAG_FORCE_LAYOUT added this flag final boolean forceLayout = (mPrivateFlags |= & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; // Optimize layout by avoiding an extra EXACTLY pass when the view is // already measured as the correct size. In API 23 and below, this // extra pass is required to make LinearLayout re-distribute weight. //If the size changes before and after measurement final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec; //If both width and height are exact values final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; //If the height and width in the current View are the same as the width and height passed in by the current parent View final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); //If the following conditions require layout final boolean needsLayout = specChanged && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize); //If requestLayout is called or needsLayout is satisfied, the measurement is performed if (forceLayout || needsLayout ) { // first clears the measured dimension flag //Delete the mark to start measurement mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; //Set rtl, which is the configuration read from right to left resolveRtlPropertiesIfNeeded(); //Extract the cache index from the cache. Then judge whether there is a cache according to this int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back //If there is no cache, just go to onMeasure onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { //If there is a cache, go to the cache setting long value = 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; } // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer //No matter whether the cache goes or not, the measurement result must be set, that is, setMeasuredDimension must be called //Pflag after setMeasuredDimension call_ MEASURED_ DIMENSION_ The set flag is added //Otherwise, throw the exception if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } //After the measurement is completed, start adding the flag bit pflag of the request of the layout_ LAYOUT_ Required to prepare the layout operation mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } //Set your own width and height mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; //Add cache mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension }
I have fully commented on the above code
Generally speaking, this method mainly does one thing, calling its own onMeasure. This onMeasure is a method that needs to be overridden by subclasses. This design enhances the extensibility of View
4. View#onMeasure() or ViewGroup subclass #onMeasure()
If the View here is not a ViewGroup, for example, a TextView, just go to onMeasure, and this is the end,
If it's ViewGroup, because ViewGroup doesn't override onMeasure, I'll see how subclasses do it
Here, I'll take the Framelayout as an example. I'll just talk about it briefly. I'll talk about this method in detail later in the example
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; //The following for loop mainly traverses the subclass view, and then distinguishes the measurement of the subclass view //After measuring the subview, we return to the previous cycle for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { //Call the measureChildWithMargins method of ViewGroup measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { final int width = Math.max(0, getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } final int childHeightMeasureSpec; if (lp.height == LayoutParams.MATCH_PARENT) { final int height = Math.max(0, getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }
5,ViewGroup#measureChildWithMargins
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //This method will calculate the appropriate measurement values according to the passed parameters, which are related to match,warp, or accurate values in the xml layout 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); //The core code calls the measure method of the sub view and loops back child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
This is the end of the whole process analysis
3. Interpretation of MeasureSpec
The MeasureSpec class appears repeatedly in the above analysis. Let's see what this class looks like
This class is the internal static class of View
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; } return makeMeasureSpec(size, mode); } @MeasureSpecMode public static int getMode(int measureSpec) { //noinspection ResourceType return (measureSpec & MODE_MASK); } 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); }
The subtlety of MeasureSpec is that it can identify the mode of size and the value of size through only one int type, which has less memory overhead and simple parameter passing
The width and height of MeasureSpec are int type. The int type is 4 bytes, 32 bits. The upper 2 bits are mode and the lower 30 bits are values
for instance
00000000 00000000 00000000 00000001 00101100 the upper 2 digits are 00, which means UNSPECIFIED, and the lower 30 digits are 300
01000000 000000 000000 01 00101111 the high 2 digits are 01, which means actual, and the low 30 digits are 303
10000000 00000000 00000001 00102100 the top two digits are 10, representing at_ The most low 30 bits are the value 284
What if we want to extract the mode in this int number?
Taking 00000000 00000000 00000001 00101100 bits as an example, we only need 110000000000 00000000 00000000 00000000
Do bitwise AND & operations to get the mode value
Actually, MeasureSpec does the same, mode_ Mask = 3 < < 30 is actually the 11000000 00000000 00000000 00000000 above
@MeasureSpecMode public static int getMode(int measureSpec) { //noinspection ResourceType return (measureSpec & MODE_MASK); }
What about size? The same operation as extracting mode above is OK
public static int getSize(int measureSpec) { //Here this ~ MODE_MASK=00111111 11111111 11111111 11111111 //Press bit & to get the following size return (measureSpec & ~MODE_MASK); }
What if you combine mode and size into an int?
//The parameter sets the value range through annotation. Here, the size is not a complete int range, because the upper 2 bits are occupied public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode) { //sUseBrokenMakeMeasureSpec doesn't matter. It's a code problem of new and old versions. The following method was used after sdk 17 if (sUseBrokenMakeMeasureSpec ) { return size + mode; } else { //After the above explanation, I believe you can understand the following writing return (size & ~MODE_MASK) | (mode & MODE_MASK); } }
Let's talk about this mode again
- When the specmode is UNSPECIFIED, the expected size is used;
- When specmode is actual, use the specified determined value (usually the value determined in layoutparams);
- When specmode is at_ During most, judge whether the self expectation exceeds the limit of the parent view. If not, use the self expected value. If not, use the maximum value of the parent view limit.
4. Examples
Here we will use the onMeasure of Framelayout to explain and see how it is implemented
The purpose of this method is to calculate the size of itself and the size of sub views
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); //The function of this is to mark whether the long size of the current Framelayout is accurate //If so, there is no need to measure the sub view inside for the second time //If not, for example, it contains at_most, you need to accurately measure yourself and measure the match in the sub view again_ Child view of parent //You can understand the whole process. Is it this logic final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; //Antecedent set mMatchParentChildren.clear(); //This is to constantly obtain the maximum value, and finally set your own size int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); //Measure the visible or invisible views. Here, remember that the invisible views should also be measured. We know that the invisible also takes up space //mMeasureAllChildren if the flag of forced measurement of all views is set, all measurements are required if (mMeasureAllChildren || child.getVisibility() != GONE) { //To measure the size of the subview, you need to calculate the padding of the Framelayout and the margin of the Child //After the calculation, the child will have the width and height. This method will trigger the measure tree of the sub View to measure all the views below //There is a very important method in ViewGroup: getChildMeasureSpec(int spec, int padding, int childDimension) //Subclasses that inherit the ViewGroup can generally call measureChildWithMargins directly. This rule is a unified setting of the android system, and users generally do not change it by themselves measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); //Get maximum maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { //As long as one of the width and height of the subview is equal to MATCH_PARENT needs to be added and re measured later //Because the size of the Framelayout was not known before, the measurement was inaccurate mMatchParentChildren.add(child); } } } } // Account for padding too //According to its own padding, this is the of the foreground map maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width //The size is calculated according to the maximum and minimum values set by itself maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width //If the foreground map is set, the size of the foreground map should be taken into account final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } /* resolveSizeAndState: public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { The size above is the size you want final int specMode = MeasureSpec.getMode(measureSpec); //specSize Is the size given by the parent view in combination with its own settings final int specSize = MeasureSpec.getSize(measureSpec); final int result; switch (specMode) { //This is as big as possible, case MeasureSpec.AT_MOST: //The following is the one with the smallest value if (specSize < size) { result = specSize | MEASURED_STATE_TOO_SMALL; } else { result = size; } break; //If the size is determined, follow the settings handed down above case MeasureSpec.EXACTLY: result = specSize; break; //If there are no requirements, set the sub view to calculate the desired size case MeasureSpec.UNSPECIFIED: default: result = size; } return result | (childMeasuredState & MEASURED_STATE_MASK); } This method is to calculate the appropriate size according to the mode, which you can experience */ //This method is to set the size of the Framelayout itself setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); //The following is the View set to be re measured. The reason has been mentioned above count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { //The value of getMeasuredWidth has been measured before, which is different from the previous one, so it needs to be measured again //When the width of the subview is MATCH_PARENT, then its own width is the following calculation. It should be close to the parent view as much as possible final int width = Math.max(0, getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } final int childHeightMeasureSpec; if (lp.height == LayoutParams.MATCH_PARENT) { final int height = Math.max(0, getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } //Remeasure child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }
Above, we called the measureChildWithMargins method. After tracing, we will eventually call getChildMeasureSpec of ViewGroup
Method, which is a general measurement rule of the subclass of ViewGroup. This method mainly calculates a reasonable and suitable size through the recommended size of the parent view and its own settings
Let's take a look
//The childDimension parameter is the child's own set size and the desired size //childDimension includes both specific dimensions and MATCH_PARENT,WRAP_CONTENT public static int getChildMeasureSpec(int spec, int padding, int childDimension) { //Get the mode from the spec. as mentioned earlier, this mode should have three types: 0, 1 and 2 int specMode = MeasureSpec.getMode(spec); //Get the size from the spec, which is the real value int specSize = MeasureSpec.getSize(spec); //This size is the space left for childview after the padding is removed. This padding includes the padding of the parent view and its own //margin, which is the maximum value that can be obtained when the sub view is as large as possible int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; //This method is mainly right switch (specMode) { // Parent has imposed an exact size on us //This means that the size of the parent view is determined and is an accurate value case MeasureSpec.EXACTLY: if (childDimension >= 0) { //If the size of the parent view is determined, if the child view sets the size you want to determine, it will be so large that it can be exceeded resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. //If the parent view size is determined, the child view has match set_ Parent, which means the maximum possible, but no more than the parent view //Therefore, this size can only be the size padding of the parent view //mode should be set to actual 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. //If the parent view size is determined, the child view has wrap set_ CONTENT,WRAP_ If content is an adaptive size, the maximum size of the child view cannot exceed the space given by the parent view. Set the size according to your own situation //So this mode=AT_MOST, 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 //If the sub view has set a precise value, it should set its own value. The mode is also effective 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. //Because the parent view of this child view is upward or upward, wrap must be set for one of the parent views_ Content, so you should also set it to //AT_MOST //The size is the maximum range specified by the parent view 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) { //If the sub view has set a precise value, it should set its own value. The mode is also effective // 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 //There is a susezerounspecified measurespec here, but it returns 0 in UNSPECIFIED mode //This is when SDK < 23. If it is greater than 23, it returns size resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
Summarize the rules of getChildMeasureSpec:
1. If the subview itself sets a clear numerical size, the numerical result of the subview is this value, and the mode is actual
2. If the parent view is AT_MOST, except for the case of 1, the value of the child view is the maximum value given by the parent view, and the mode is AT_MOST
So at_ The most mode will be passed to the sub View until the View with the exact value set is encountered
3. If the parent View is UNSPECIFIED, except for 1, the value of the child View is the maximum value given by the parent View, and the mode is UNSPECIFIED. Therefore, the UNSPECIFIED mode will be passed to the child View until the View with the exact value set is encountered
4. In addition to setting your own size accurately, the value will be the maximum value left by the parent view to the child view
4, Summary
1. The measure process in View is triggered from the root View, and then continuously traverses the sub View measurement layer by layer. We should deeply understand this process
2. When the child view calculates its own size, it should consider the control and mode given by the parent view and the value set by itself. ViewGroup#getChildMeasureSpec is implemented here
3. The MeasureSpec design uses an int to identify mode and size, which is implemented by bit operation. It is still very subtle
This concludes the measurement analysis of this View