- The Android View is a tree structure, and the size of the View, the size of the ViewGroup, affects each other, and there are two important parameters
1.LayoutParams Layout parameters
1. Understand the LayoutParams
- LayoutParams are the layout parameters by which the child View tells the parent container (ViewGroup) how to place itself.
- Each ViewGroup subclass has its own corresponding LayoutParams class, typical such as LinearLayout. LayoutParams and
Framelayout.layoutparams, etc. are internal classes of ViewGroup subclasses.
- MarginLayoutParams is all about outer spacing. MarginLayoutParams only adds support for up, down, left, and right outer spacing compared to LayoutParams. In fact, most of the LayoutParams implementation classes are inherited from MarginLayoutParams, as almost all parent containers support child View spacing.
2. Priorities
- MarginLayoutParams is basically adding 4 outer Spaces up, down, left and right. In the constructor, the margin attribute is obtained; If the value is illegal, get the horizontalMargin; If the value is illegal, get the leftMargin and rightMargin attributes (same as verticalMargin, topMargin, and bottomMargin). From this we can summarize the priority of these attributes
- Priority Margin > HorizontalMargin and VerticalMargin > LeftMargin and RightMargin, TopMargin and BottomMargin
3. LayoutParams parameters
- Define the View in XML
Generate the View instance object directly in Java code
- 3.1. If the XML layout contains a concrete DP, then layoutParams is greater than 0.
- 3.2. If it is MATCH_PARENT
public static final int MATCH_PARENT = -1;
- 3.3. If WRAP_CONTENT
public static final int WRAP_CONTENT = -2;
4.addView
/** * Override method 1: Add a child View * If the child View does not already have LayoutParams, set the default LayoutParams for the current ViewGroup of the child View **@param child
*/
@Override
public void addView(View child) {
addView(child, -1);
}
/** * Override method 2: Add a child View at the specified location * if the child View does not already have LayoutParams, set the current ViewGroup default LayoutParams for the child View **@param child
* @paramIndex View at the position to be added to the ViewGroup (-1 means added to the end) */
@Override
public void addView(View child, int index) {
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}
/** * Override method 3: Add a child View * using the default LayoutParams of the current ViewGroup **@param child
* @paramWidth is passed in as width * for LayoutParams@paramHeight is passed as the height */ of LayoutParams
@Override
public void addView(View child, int width, int height) {
final LayoutParams params = generateDefaultLayoutParams();
params.width = width;
params.height = height;
addView(child, -1, params);
}
/** * override method 4: Add a child View **@param child
* @paramParams uses the LayoutParams */ passed in
@Override
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
}
/** * Override method 5: Add a child View, **@param child
* @paramIndex View is the position to be added to the ViewGroup *@paramParams uses the LayoutParams */ passed in
@Override
public void addView(View child, int index, LayoutParams params) {
super.addView(child, index, params);
}
Copy the code
5. GenerateDefaultLayoutParams method
- AddView source to find, to find generateDefaultLayoutParams method, to obtain the default LayoutParams
- ViewGroup generateDefaultLayoutParams method
protected LayoutParams generateDefaultLayoutParams(a) {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
Copy the code
6. CheckLayoutParams method
- AddView source code all the way down, you can find checkLayoutParams method
- ViewGroup checkLayoutParams method
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
returnp ! =null;
}
Copy the code
- Non-viewgroup checkLayoutParams method, specific source code to see
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LinearLayout.LayoutParams;
}
Copy the code
2.MeasureSpec
1. Measure the View size (onMeasure)
- The size of a View is not only determined by itself, but also affected by the parent control. Its size may also be affected by the child View, and the onMeasure method will be called several times.
2. The MeasureSpec parameters
- MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = 32;
- Source code we can see the definition
3. Obtain parameters
@Override
protected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec){
// Retrieve the exact width
int widthsizeMeasureSpec.getSize(widthMeasureSpec);
// Select the width measurement mode
int widthmodeMeasureSpec.getMode(widthMeasureSpec);
// Retrieve the exact height
int heightsizeMeasureSpec.getSize(heightMeasureSpec);
// Select the height measurement mode
int heightmodeMeasureSpec.getMode(heightMeasureSpec);
}
Copy the code
4. Three measurement modes
- There are three modes of measurement
model | Binary value | describe |
---|---|---|
UNSPECIFIED | 00 | By default, the parent control does not impose any restrictions on the child view, which can be set to any size. |
EXACTLY | 01 | Indicates that the parent control has specified the exact size of the child View. |
AT_MOST | 10 | The size of the child View is not limited, but there is an upper limit, the upper limit is generally the size of the parent View. |
MeasureSpec(getChildMeasureSpec);
- ViewGroup: getChildMeasureSpec: Quantum View getChildMeasureSpec: Quantum View getChildMeasureSpec: Quantum View
- The parent VIewGroup’s MeasureSpec, plus its own LayoutParams layout parameters, is required to measure the child view.
- Do not call super.onMeasure(widthMeasureSpec, heightMeasureSpec) if you change the width and height of the View;
To call setMeasuredDimension(WidthSize, HeightSize); This function.
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// The measurement mode of the parent control
int specMode = MeasureSpec.getMode(spec);
// Measure the size of the parent control
int specSize = MeasureSpec.getSize(spec);
// Subtract the inner margin
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:
// Parent layout exact mode
if (childDimension >= 0) {
// The layout parameters of the subview also determine the size, then the subview mode is precise mode
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
// The child view is MATCH_PARENT, which is also exact mode. The child view is as big as the parent layout
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.
// The child view is WRAP_CONTENT, which cannot exceed the size of the parent layout, which is AT_MOST
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break; . . .return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code
- I think you’ve all seen this picture in the Android Development Art book
6. Determine View size (onSizeChanged)
- This function is called when the view size changes.
- Since the size of a View is controlled not only by the View itself, but also by the parent control, it is best to use the onSizeChanged callback provided by the system to determine the size of the View.
7. Get measured size (getMeasuredWidth)
- The corresponding value can be obtained after the measure() process ends;
- Set by setMeasuredDimension() method.
8. GetWidth and height (getWidth)
- It is available after the layout() procedure is finished;
- It’s calculated by subtracting the left coordinate from the right coordinate of the view
3. Specific use
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Width and height of the parent layout
int viewGroupWidth = MeasureSpec.getSize(widthMeasureSpec);
int viewGroupHeight = MeasureSpec.getSize(heightMeasureSpec);
// Iterate over all child Views
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
// If view is visible
if (childView.getVisibility() == View.VISIBLE) {
// The layout parameters of the child view
LayoutParams childLayoutParams = childView.getLayoutParams();
// The child view's measureSpec, the parent layout's measureSpec, the parent layout's inner margin, and the child view's size (greater than 0 is the exact size, -1 is match_parent,-2 is wrap_content)
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, leftPadding + rightPadding, childLayoutParams.width);
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, topPadding + bottomPadding, childLayoutParams.height);
// The subview calls measure to get the exact width and height
childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// The width and height of the subview
int childMeasureWidth = childView.getMeasuredWidth();
int childMeasureHeight = childView.getMeasuredHeight();
}
// remeasure the ViewGroup
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = widthMode == MeasureSpec.EXACTLY ? viewGroupWidth : viewGroupNeedWidth;
int realHeight = heightMode == MeasureSpec.EXACTLY ? viewGroupHeight : viewGroupNeedHeight;
setMeasuredDimension(realWidth, realHeight);
}
Copy the code
4. Reference materials
- Exploring the Art of Android Development
- Android custom View advanced