问题:给MyView设置默认宽高的话,在measure的过程中mode变为EXACTLY,而且不能自适应content的大小。查看TextView的源码,发现其中不仅是对三种模式进行单独处理,而且还让AT_MOST做了自适应content,这个问题有点复杂,留待以后解决
View的测量会回调onMeasure方法,因此首先要复写onMeasure方法,这个方法的作用进行宽高的测量,然后必须调用setMeasuredDimension进行设置,不然会触发IllegalStateException异常
不复写此方法,默认采用EXACTLY模式测量,而EXACTLY只支持match_parent和指定的尺寸,指定为wrap_content的话无效,因为wrap_content要有一个默认值,因此系统会直接设置成match_parent.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
系统回调onMeasure的时候,是从布局文件中读取宽高的设置,比如指定match_parent和具体尺寸的话,显然这里的mode为EXACTLY,MeasureSpec这里不赘述,官网解释View.MeasureSpec,这种情况下不需要我们处理,因为符合默认的测量模式。
但是我们如果希望控件自适应内容呢?即wrap_content。又该怎么办呢?前面已经提到在布局文件设置wrap_content无效,大家可以自己测试一下。
解决办法:之所以设置无效,是因为在onMeasure的时候,默认以EXACTLY来处理,那么我们只要自己写一个测量方法就可以了。
下面给出了测量宽和高的两个方法
measureWidth
和measureHeight
,只需要在模式不是EXACTLY的时候进行处理就可以了
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
private int measureWidth(int widthMeasureSpec) {
Log.i("measureWidth"," "+MeasureSpec.toString(widthMeasureSpec));
int result=200;//指定默认大小
if((MeasureSpec.getMode(widthMeasureSpec)!=MeasureSpec.EXACTLY)){
result = Math.min(result,MeasureSpec.getSize(widthMeasureSpec));
return result;
}
return MeasureSpec.getSize(widthMeasureSpec);
}
private int measureHeight(int heightMeasureSpec) {
Log.i("measureHeight"," "+MeasureSpec.toString(heightMeasureSpec));
int result=200;//指定默认大小
if((MeasureSpec.getMode(heightMeasureSpec)!=MeasureSpec.EXACTLY)){
result = Math.min(result,MeasureSpec.getSize(heightMeasureSpec));
return result;
}
return MeasureSpec.getSize(heightMeasureSpec);
}
日志:
通过日志发现,开始的测量模式为AT_MOST,因为我们设置的是wrap_content,然后再自己定义的测量方法中指定了默认大小,因此变为了EXACTLY。
效果:
实现自己的测量方法前
实现自己的测量方法后
可以看到实现自己的方法后,wrap_content变得有效,默认值为200dp。