最近在学习自定义View,在这里对学到的一些东西做个记录,来加深一下记忆。
首先看View的onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
这里面有主要有两个方法其中setMeasuredDimension方法是设置View的宽高的,而使wrap_content属性无效的原因要看第二个方法就是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;
}
在这里还要说一下getDefaultSize传入的两个参数getSuggestedMinimumWidth(), widthMeasureSpec,这里了解一下宽的,高是一样的。
其中getSuggestedMinimumWidth方法源码如下:
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
意思大概就是如果View没背景就返回mMinWidth,如果View设置了背景就返回mMinWidth和mBackground的最小宽度之间最大的那一个。
widthMeasureSpec则是一个32位的int值,它的高2位代表了SpecMode,低30位代表了SpecSize;其中SpecMode指的是测量模式,SpecSize指的是测量大小,而SpecMode只有三种模式分别是UNSPECIFIED、AT_MOST和EXACTLY。这种模式的大致含义如下所示:
- UNSPECIFIED 未指定模式,View想多大就多大,父容器不做限制。
- AT_MOST 最大模式,差不多对应wrap_content属性。
- EXACTLY 精确模式,对应match_parent属性和具体的数值。
接下来在回过头看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://未指定模式下直接返回传进来的的size值
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY://AT_MOST和EXACTLY模式下都返回获取到的specSize 值
result = specSize;
break;
}
return result;
}
可以看到AT_MOST和EXACTLY模式都返回获取到的specSize 值,即View在这两种模式下宽高都取决于specSize。由此可以知道当我们直接继承View来自定义View时,对与我们的这个自定义的View来说wrap_content和match_parent属性的效果是相同的。因此当我们直接继承View来自定义View时一定要重写onMeasure方法,来针对wrap_content属性进行处理。例如可以在onMeasure方法中给wrap_content属性指定一个默认的值,代码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽高的测量模式
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
//获取宽高的测量大小
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST & heightSpecMode == MeasureSpec.AT_MOST) {//当宽高的测量模式都为AT_MOST时指定默认宽高900
setMeasuredDimension(900, 900);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {//当宽的测量模式都为AT_MOST时指定默认宽900
setMeasuredDimension(900, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {//当高的测量模式都为AT_MOST时指定默认高900
setMeasuredDimension(widthSpecSize, 900);
}
}