View的测量
Android 系统在绘制View前,需要对View进行测量,即告诉系统该画一个多大的View,这个过程在 onMeasure() 方法中进行。Android系统给我们提供了一个帮助我们测量View的类—-MeasureSpec类,MeasureSpec是一个32为的值,其中高2位为测量的模式,低30为为测量的大小。
测量的模式有三种:
EXACTLY
精确模式,当我们将控件的layout_width 属性或layout_height属性指定为具体数值时,比如:android:layout_width = “100dp” ,或者指定为 match_parent 属性时(占据父View的大小),系统使用的是 EXACTLY 模式。AT_MOST
最大模式,当我们将控件的layout_width 属性或layout_height属性指定为 wrap_content 时,控件大小一般随着控件的子控件或内容的变化而变化,此时控件的尺寸只要不超过父控件永许的最大尺寸即可。UNSPECIFIED
不指定其大小测量模式,View想多大就多大,通常情况下在绘制自定义View时才会使用。
View类默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式。控件可以响应指定的具体宽高值或者是match_parent 属性,而如果要让自定义View支持 wrap_content 属性,那么就必须重写 onMeasure() 方法来指定 wrap_content 时的大小。
通过MeasureSpec这一个类,我们就获取了View的测量模式和View想要绘制的大小,有了这些信息,我们就可以控制View最后显示的大小。
接下来我们看一个简单的实例,演示如何进行View的测试量,首先我们重写onMeasure()方法。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
通过查看super.onMeasure()方法,可以发现,系统最终会调用 setMeasuredDimension(int measuredWidth, int measuredHeight) 方法将测量后的宽高值设置进去,从而完成测量工作。所以在重写 onMeasure() 方法后,最终要做的工作就是把测量后的宽高值作为参数设置给 setMeasuredDimension()方法。
所以重写的 onMeasure()方法代码如下所示:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
measureWidth(widthMeasureSpec),
measureheight(heightMeasureSpec));
}
在onMeasure()方法中,我们调用自定义的measureWidth()方法和measureheight()方法,分别对宽高进行重新定义,参数则是宽和高的MeasureSpec 对象,MeasureSpec 对象中包含了测量的模式和测量的大小。
下面我们就来看一下如何自定义测量值:
1. 从MeasureSpec 对象中提取出具体的测量模式和大小
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
- 通过判断测量的模式,给出不同的测量值,当specMode为 EXACTLY 时,直接使用指定的 specSize 即可,当specMode为AT_MOST或UNSPECIFIED时,需要给它一个默认的大小(特别的,如果指定wrap_content属性,即AT_MOST模式,则需要给它一个默认的大小与specSize中最小的一个来作为最后的测量值)。measureWidth方法如下:
private int measureWidth(int widthMeasureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 300;
if (specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
private int measureheight(int heightMeasureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 300;
if (specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
通过以上两个方法,我们就完成了对宽和高的自定义。
在布局文件中,指定确定的宽高值为400px,程序运行效果如图1所示
当指定宽高属性为 match_patent 属性时,程序运行效果如图2所示
当指定宽高属性为 wrap_content 属性时,如果不重写 onMeasure()方法,那么系统就不知道该默认多大的尺寸,因此,他就会默认填充整个父布局,所以重写onMeasure()方法的目的,就是为了能够给View一个wrap_content属性下的默认大小,程序运行效果如图2所示
图1:
图2:
图3
可以发现,当指定wrap_content属性时,View就获得了额一个默认值 200px,而不再是填充父布局。
以上就是View大小的测量的相关内容。
以上内容来之Android 群英传,感谢前辈们的付出。