How do I traverse and draw a View tree? In previous articles, the invalidate () function details (combined with the latest Android 4.0.4 source code) mentioned in invalidate () will finally initiate a View tree traversal request and execute javasmtraersal () performTraersal () is the core function for traversing and drawing the View tree. The internal main logic is to determine whether to re-measure the View Size (measure ), whether layout or draw is required ). The measure process is a prerequisite for traversal. layout and draw can be performed only after measure ), because the measurement size of each View calculated during the measure process needs to be used in the layout process, and the draw process requires layout to determine the position of each view before drawing. Next we will mainly discuss the main process of measure, which is difficult to understand compared with layout, draw, and measure.
When compiling the layout xml file, we will encounter the layout_width and layout_height attributes. For these two attributes, we have three options: assign a value to a specific value, match_parent or wrap_content, the measure process is used to process match_parent or wrap_content. If layout specifies that the layout_width and layout_height values of all views must be assigned a specific value, the measure process is not necessary, however, there must be a reason for google to consider adding match_parent or wrap_content to Android design. They will make the layout more flexible.
First, let's look at several key functions and parameters:
1. public final void measue (int widthMeasureSpec, int heightMeasureSpec );
2. protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec );
3. protected void measureChildren (int widthMeasureSpec, int heightMeasureSpec)
4. protected void measureChild (View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)
5. protected void measureChildWithMargins (View child, int parentWidthMeasureSpec, int widthUsed,
Int parentHeightMeasureSpec, int heightUsed)
Next, let's look at the source code of the measure and onMeasure functions in the View class:
[Java]
Public final void measure (int widthMeasureSpec, int heightMeasureSpec ){
If (mPrivateFlags & FORCE_LAYOUT) = FORCE_LAYOUT |
WidthMeasureSpec! = MOldWidthMeasureSpec |
HeightMeasureSpec! = MOldHeightMeasureSpec ){
// First clears the measured dimension flag
MPrivateFlags & = ~ MEASURED_DIMENSION_SET;
If (ViewDebug. TRACE_HIERARCHY ){
ViewDebug. trace (this, ViewDebug. HierarchyTraceType. ON_MEASURE );
}
// Measure ourselves, this shocould set the measured dimension flag back
OnMeasure (widthMeasureSpec, heightMeasureSpec );
// Flag not set, setMeasuredDimension () was not invoked, we raise
// An exception to warn the developer
If (mPrivateFlags & MEASURED_DIMENSION_SET )! = MEASURED_DIMENSION_SET ){
Throw new IllegalStateException ("onMeasure () did not set"
+ "Measured dimension by calling"
+ "SetMeasuredDimension ()");
}
MPrivateFlags | = LAYOUT_REQUIRED;
}
MOldWidthMeasureSpec = widthMeasureSpec;
MOldHeightMeasureSpec = heightMeasureSpec;
}
Because the function prototype has a final field, measure has no intention of inheriting the quilt class, that is, the measure process is fixed, and measure calls the onMeasure function, so the real variable is the onMeasure function. The default Implementation of onMeasure is very simple. The source code is as follows:
[Java]
Protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec ){
SetMeasuredDimension (getDefaultSize (getSuggestedMinimumWidth (), widthMeasureSpec ),
GetDefaultSize (getSuggestedMinimumHeight (), heightMeasureSpec ));
}
The default Implementation of onMeasure only calls setMeasuredDimension. The setMeasuredDimension function is a key function that assigns values to the member variables mMeasuredWidth and mMeasuredHeight of the View, the main purpose of measure is to assign values to the mMeasuredWidth and mMeasuredHeight of each View in the View tree. Once the two variables are assigned, the measurement of the View is completed.
[Java]
Protected final void setMeasuredDimension (int measuredWidth, int measuredHeight ){
MMeasuredWidth = measuredWidth;
MMeasuredHeight = measuredHeight;
MPrivateFlags | = MEASURED_DIMENSION_SET;
}
For a non-ViewGroup View, you can call the default measure --> onMeasure above to complete the View measurement. Of course, you can also reload onMeasure and call setMeasuredDimension to set the layout of any size, but this is generally not the case, because this practice is too "dictatorship". As to why "dictatorship", I will understand it after reading this article.
For the subclass of ViewGroup, The onMeasure function is often reloaded to take charge of the measure work of its children. Do not forget to call setMeasuredDimension to set its own mMeasuredWidth and mMeasuredHeight during the reload. If we do not need to depend on the size of the subview during layout, we do not need to overload onMeasure. However, we must reload onLayout to arrange the position of the subview. This will be introduced in the next blog.
Let's take a look at the two parameters in measue (int widthMeasureSpec, int heightMeasureSpec). These two parameters are the measurement specifications provided by the parent view, respectively, when the parent view calls the measure function of the Child view to measure the child view, these two parameters are passed in. The two parameters and the LayoutParams of the child view are used to jointly determine the measurement specifications of the Child view, this process is embodied in the measureChildWithMargins function of ViewGroup, which will be introduced later.
The value of the MeasureSpec parameter is int type, which can be divided into high 32-bit and low 16. The high 32-bit stores specMode, and the low 16-bit represents specSize. specMode is divided into three types:
1. MeasureSpec. UNSPECIFIED. The parent view does not impose any restrictions on the Child view, and the Child view can obtain any desired size;
2. MeasureSpec. EXACTLY. The size of the parent view is the size specified in specSize;
3. MeasureSpec. AT_MOST. The size of the sub-view is the maximum size in specSize.
The preceding restrictions only apply to the parent view. The size of the Child view is as described in MeasureSpec. However, the specific size of the Child view depends on multiple aspects.
The ViewGroup defines measureChildren, measureChild, and measureChildWithMargins to measure the child views. The difference between measureChildren and measureChildWithMargins is whether to use margin and padding as the size of the child views, we mainly analyze the execution process of measureChildWithMargins:
[Java]
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 );
}
In general, this function is to adjust the measureSpec parameter provided by the parent view (combined with its own LayoutParams parameter), and then call child. use the getChildMeasureSpec function to adjust the parameters. The procedure is as follows:
[Java]
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:
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 shoshould
// Be
ResultSize = 0;
ResultMode = MeasureSpec. UNSPECIFIED;
} Else if (childDimension = LayoutParams. WRAP_CONTENT ){
// Child wants to determine its own size... find out how
// Big it shocould be
ResultSize = 0;
ResultMode = MeasureSpec. UNSPECIFIED;
}
Break; www.2cto.com
}
Return MeasureSpec. makeMeasureSpec (resultSize, resultMode );
}
The general idea of getChildMeasureSpec is to get specMode and specSize through the MeasureSpec parameter provided by its parent view, and get the calculated specMode and childDimension of the subview (defined in layout_width and layout_height) to calculate its own measureSpec. if it contains a sub-view, the calculated measureSpec will be used as a parameter to call its sub-view measure function, and also as a parameter to call setMeasuredDimension, if it does not contain a sub-view, the default Implementation of onMeasure will be called by default, and setMeasuredDimension will be called. The parameters of this function are calculated here.
Conclusion: from the above description, the most decisive part is the View designer, because the designer can call setMeasuredDimension to determine the final size of the View, for example, call setMeasuredDimension (100,100) set the mMeasuredWidth and mMeasuredHeight of the view to 100,100. The size provided by the parent view and the layout_width and layout_height set by the programmer in xml do not work at all, of course, a good design generally sets the size of mMeasuredWidth and mMeasuredHeight Based on the measureSpec of the sub-view, and has respected the programmer's intent.
Author: zjmdp