ViewGroup
的宽高,不是内容的宽高View
的宽高,不是内容的宽高View
与ViewGroup
不同测量getDefaultSize
的实现View
的宽 对应的属性为: mMeasuredWidth
View
的高 对应的属性为: mMeasuredHeight
ViewGroup
开始measure
方法,在measure
方法中回调onMeasure
onMeasure
中对子节点进行了测量,则测量子节点ViewGroup
测量的是ViewGroup
的宽高,不是内容的宽高在ViewGroup
嵌套View
的情况下,测量的是ViewGroup
的宽高,不是ViewGroup
中内容的宽高。
由上图可知,测量的数据上实际上是:
parentWidth
parentHeight
因为内部内容的高度,或者宽度是可以大于容器的宽高的,所以可以这样认为,只有当宽高设置为wrap_content
时,ViewGroup
的测量才会与内部的View
有关
View
测量的是View
的宽高,不是内容的宽高一般情况下,View
是用来显示一些内容的,那么测量View
时,测量的也是View
的宽高,而不是内容的宽高
由上图可知,测量的数据上实际上是:
width
height
因为内部内容的高度,或者宽度是可以大于容器的宽高的,所以如果如果想根据内容变化,需要计算内容的宽度和高度。
onMeasure
中,根据实际情况,实现测量操作setMeasuredDimension
设置到View
的属性上测量过程实际上是根距实际的需求,定义算法来算出宽高,也是测量过程中比较重要的一步。由于measure
方法是不可重新写的,所以,测量过程一般都是通过重写onMeasure
方法来定义。
View
与ViewGroup
不同测量View
与ViewGroup
不同,因为View
没有子节点,只需要测量即可。而不同的ViewGroup
会可能有不同的测量规则,因为它可能根据子节点来变化,所以一般ViewGroup
需要测量子节点,而且还需要根据子节点的宽高来计算自己的宽高。ViewGroup
提供了测量子节点的方法。
一般情况下,ViewGroup
需要自定义测量过程,并且需要测量子节点。
在重写onMeasure
时发现,传递了两个int
值,这两个值是:int widthMeasureSpec, int heightMeasureSpec
,实际上这是一个32位int值,高两位代表SpecMode
,低30位代表SpecSize
SpecMode
指的是测量模式SpecSize
指的是在某种测量模式下的规格大小SpecMode
有三个值,定义在MeasureSpec
类中
UNSPECIFIED
,父容器不对View
有任何限制,用于系统内部EXACTLY
,精确值,这个时候View的最终大小就是SpecSize
所指定的值, 对应于LayoutParam
中的match_parent
或者直接指定具体值。AT_MOST
,父容器制定了一个可用大小,即SpecSize
,View
的大小不能大于这个值,对应于LayoutParams
中的wrap_content
SpecMode
与SpecSize
呢??protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 获取SpecMode
MeasureSpec.getMode(widthMeasureSpec);
MeasureSpec.getMode(heightMeasureSpec);
// 获取SpecSize
MeasureSpec.getSize(widthMeasureSpec);
MeasureSpec.getSize(heightMeasureSpec);
......
}
在onMeasure
时,ViewGroup
一般都需要实现此方法,并且要测量子节点。而View
一般直接实现指定测量出的宽高即可。
但是有时候业务需求,需要自定义测量宽高,这时可以根据不同的测量模式进行不同的实现:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int pWidth=0;
int pHeight=0;
// 获取测量模式
int widthMeasureSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMeasureSpecMode = MeasureSpec.getMode(heightMeasureSpec);
switch (widthMeasureSpecMode) {
case MeasureSpec.UNSPECIFIED:
........ // 自定义实现
break;
case MeasureSpec.AT_MOST:
........ // 自定义实现
break;
case MeasureSpec.EXACTLY:
........ // 自定义实现
break;
}
switch (heightMeasureSpecMode) {
case MeasureSpec.UNSPECIFIED:
........ // 自定义实现
break;
case MeasureSpec.AT_MOST:
........ // 自定义实现
break;
case MeasureSpec.EXACTLY:
........ // 自定义实现
break;
}
setMeasuredDimension(pWidth, pHeight);
}
当回调onMeasure
时,如果想设置自定义实现的测量后的结果,可以通过protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)
来设置,这个方法必须在onMeasure()
时被调用!
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
看到,在View的默认实现中调用了getDefaultSize(int size, int measureSpec)
方法用来获取宽高,
getDefaultSize
的实现public static int getDefaultSize(int size, int measureSpec) {
int result = size;
// 获取测量模式
int specMode = MeasureSpec.getMode(measureSpec);
// 获取测量到的大小
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Android从整体到局部系列视频教程戳我
我使用的装备:程序员必备 | 不伤关节 | 手感好 | 静电容 | Plum键盘|Niz键盘 戳我