Android custom controls Series 1: How to measure the widget size
The size (width and height) of the measurement control is the first step in the development of custom controls. Only after the size is determined can the painting be started (painting on the canvas using the canvas, the controls we use are actually drawn in this way ). Of course, this size needs to be calculated based on each part of the control, such as padding, text size, and spacing.
OnMeasure of non-container controls
Next we will take a look at how to give the non-container control (that is, directly extends View) this size:
1.
@Override
2.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
3.
setMeasuredDimension(measureWidth(widthMeasureSpec),
MeasureHeight (heightMeasureSpec ));
4.
}
You can override the onMeasure () method to set the size. This method is actually called by the Container Control (LinearLayout, RelativeLayout), so that the container control knows how much space or size I need to allocate to you.
After the dimension is calculated, setMeasuredDimension () is called to set the dimension. For example, if you want to create a control with a width of 200,100 PX and a height of PX, You Can setMeasuredDimension ).
Note that the value received by setMeasuredDimension () is a px value rather than a dp value, so we cannot fix the size as above. How to do that:
The int widthMeasureSpec and int heightMeasureSpec parameter indicate the space available for the control and metadata about the space description. They are associated with android: layout_width and android: layout_height in the layout file. How to use:
01.
/**
02.
* Calculate the component width.
03.
*/
04.
private
int
measureWidth(
int
widthMeasureSpec) {
05.
int
result;
06.
int
specMode = MeasureSpec.getMode(widthMeasureSpec);
07.
int
specSize = MeasureSpec.getSize(widthMeasureSpec);
08.
09.
if
(specMode == MeasureSpec.EXACTLY) {
// Exact mode
10.
result = specSize;
11.
}
else
{
12.
Result = getDefaultWidth (); // maximum size mode. The getDefaultWidth method needs to be implemented based on the actual needs of the control.
13.
if
(specMode == MeasureSpec.AT_MOST) {
14.
result = Math.min(result, specSize);
15.
}
16.
}
17.
return
result;
18.
}
Call MeasureSpec. getMode (measureSpec) to obtain the mode (mode) for setting the size ).
There are three cases of mode:
MeasureSpec. UNSPECIFIED, MeasureSpec. EXACTLY, MeasureSpec. AT_MOST.
MeasureSpec. EXACTLY is the exact size. When we specify the layout_width or layout_height of the control as a specific value, for example, andorid: layout_width = 50dip, or FILL_PARENT, the control size is determined, all are precise dimensions.
MeasureSpec. AT_MOST is the maximum size. When the layout_width or layout_height of the control is set to WRAP_CONTENT, the control Size usually changes with the control's sub-spaces or content, in this case, the widget size must not exceed the maximum size allowed by the parent widget. Therefore, the mode is AT_MOST, and size provides the maximum size allowed by the parent control.
MeasureSpec. UNSPECIFIED is an UNSPECIFIED size. In this case, the parent control is generally AdapterView, which is passed in through the measure method.
The measureWidth () method is a fixed implementation and almost does not need to be changed. The only thing we need to implement is getDefaultWidth (). That is, when the mode is in the maximum mode, the parent container will give you the maximum space available or available. You can only set the widget size in this space. A simple implementation of getDefaultWidth () similar to TextView:
1.
private
void
getDefaultWidth(){
2.
int
txtWidth = (
int
)
this
.paint.measureText(
this
.text);
3.
return
txtWidth +
this
.paddingLeft +
this
.paddingRight;
4.
}
The width measurement above is the same as the height:
01.
/**
02.
* Computing component height
03.
*/
04.
private
int
measureHeight(
int
measureSpec) {
05.
int
result;
06.
int
specMode = MeasureSpec.getMode(measureSpec);
07.
int
specSize = MeasureSpec.getSize(measureSpec);
08.
09.
if
(specMode == MeasureSpec.EXACTLY) {
10.
result = specSize;
11.
}
else
{
12.
result = getDefaultHeight();
13.
if
(specMode == MeasureSpec.AT_MOST) {
14.
result = Math.min(result, specSize);
15.
}
16.
}
17.
return
result;
18.
}
With the above implementation, we can use the control in the layout file to customize the width and height of the control through android: layout_width and android: layout_height.
OnMeasure of Container Control
Generally, container controls inherit ViewGroup, while ViewGroup is an abstract class that does not implement onMeasure, but its subclasses have their own implementations, generally, they use the measureChildWithMargins function or other functions similar to measureChild to traverse the measurement sub-View. After all the sub-views are measured, the final size is determined based on the mode and size passed by the parent View.
When measuring a sub-View, the LayoutParams of the sub-View is obtained first, and the width and height are obtained from the sub-View. If the value is greater than 0, the sub-View is passed in a precise mode and the value is combined into MeasureSpec, if it is less than 0, it will pass its own size or remaining size to the subview, and its mode determines that there is a ing relationship in the preceding table.
ViewGroup generally calls setMeasuredDimension () to set its own size after measuring all child views.