onMeasure的作用:
确定自己的宽和高以及内部子view的宽和高。
onMeasure什么时候会被调用
当子View的父控件要放置该View的时候,父控件会传递两个参数给View——widthMeasureSpec和heightMeasureSpec。这两个参数是View可以获取的宽高尺寸和模式值混合的int数据。可以通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
MeasureSpec三种mode分析
约束 | 布局参数 | 值 | 说明 |
---|---|---|---|
EXACTLY(精确) | match_parent/具体宽高值 | 1073741824 | 当我们将控件的layout_width或layout_height指定为具体数值时如:andorid:layout_width=”50dip”,或者为match_parent是,都是控件大小已经确定的情况,都是精确尺寸。 |
AT_MOST(最大) | wrap-content | -2147483648 | 当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。 |
UNSPECIFIED(未指定) | 0 | 这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。 |
代码示例:
1、单个控件自定view重写onMeasure方法
CustomTextView.java
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
Log.e("CustomTextViewWidth", "---speSize = " + specSize + "");
switch (specMode) {
case MeasureSpec.AT_MOST:
result = (int) mPaint.measureText(mTextStr) + getPaddingLeft() + getPaddingRight();
Log.e("CustomTextViewWidth", "---speMode = AT_MOST");
break;
case MeasureSpec.EXACTLY:
Log.e("CustomTextViewWidth", "---speMode = EXACTLY");
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
Log.e("CustomTextViewWidth", "---speMode = UNSPECIFIED");
result = Math.max(result, specSize);
}
Log.e("CustomTextViewWidth", "---result = "+result);
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
Log.e("CustomTextViewHeight", "---speSize = " + specSize + "");
switch (specMode) {
case MeasureSpec.AT_MOST:
result =
(int) (-mPaint.ascent() + mPaint.descent()) + getPaddingTop() + getPaddingBottom();
Log.e("CustomTextViewHeight", "---speMode = AT_MOST");
break;
case MeasureSpec.EXACTLY:
result = specSize;
Log.e("CustomTextViewHeight", "---speSize = EXACTLY");
break;
case MeasureSpec.UNSPECIFIED:
result = Math.max(result, specSize);
Log.e("CustomTextViewHeight", "---speSize = UNSPECIFIED");
break;
}
Log.e("CustomTextViewHeight", "---result = "+result);
return result;
}
2、ViewGroup自定义view重写onMeasure方法
FlowLayout.java
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
// 如果是wrap_content,定义width,height统计FlowLayout的宽高
int width = 0;
int height = 0;
// 记录每一行的宽度与高度
int lineWidth = 0;
int lineHeight = 0;
/**
* 1、通过getChildCount,获取子View的个数view个数
*/
int childCount = getChildCount();
/**
* 2、遍历childCount,通过getChildAt获取到对应的view
*/
for (int i = 0; i < childCount; i++) {
//获取i对应的子View,通过获取他的宽高,确定
View childView = getChildAt(i);
/**
* 3、对childView进行测量
*/
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
int childWidth = childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
// 判断是否换行,如果换行则高度累加,如果不换行则宽度累加
if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
// 对比得到最大的宽度
width = Math.max(width, lineWidth);
// 重置lineWidth
lineWidth = childWidth;
// 记录行高
height += lineHeight;
// 重置lineHeight
lineHeight = childHeight;
} else {
// 宽度累加
lineWidth += childWidth;
// 得到当前行最大的高度
lineHeight = Math.max(lineHeight, childHeight);
}
// 最后一个的时候,不管是换行,还是未换行,前面都没有处理
if (i == childCount - 1) {
width = Math.max(width, lineWidth);
height += lineHeight;
}
/**
* 4、确定父布局(FlowLayout)的宽高
*/
setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth :
width + getPaddingLeft() + getPaddingRight(),
modeHeight == MeasureSpec.EXACTLY ? sizeHeight :
height + getPaddingTop() + getPaddingBottom());
}
}
完整的示例代码地址:https://github.com/hnhzy/ExampleDemo
参考文章:
https://www.cnblogs.com/yishujun/p/5560838.html
https://blog.csdn.net/xmxkf/article/details/51490283#1-onmeasure%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%BC%9A%E8%A2%AB%E8%B0%83%E7%94%A8
https://blog.csdn.net/xmxkf/article/details/51490283#%E2%91%A0-viewgroup%E4%B8%AD%E4%B8%89%E4%B8%AA%E6%B5%8B%E9%87%8F%E5%AD%90%E6%8E%A7%E4%BB%B6%E7%9A%84%E6%96%B9%E6%B3%95