之前博主学习期间写了不少自定义view 的博文,今天博主发现一篇不错的写自定义view 的文章,这里再来回味一下。
不错的文章链接
一 、确定view的大小
文章已经写的很清楚了,这里再总结一下。
首先,开发者在布局中设置的view的android:layout_width=”“android:layout_height=”“两个配置,告诉viewGroup所需要的view 的大小;然后viewGroup通过onMeasure方法传入的widthMeasureSpec和heightMeasureSpec参数告诉view开发者的需要,即protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法;最后view在重写的onMeasue方法中解译出ViewGroup对自己的要求:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
取出来的mode有三种类型,它代表viewGroup对view的态度,这里也按照我的理解大概介绍一下吧(假设现在的size = 100dp):
1.EXACTLY 表示:view你只能用100dp,原因可能是开发者对你的需要,或者是我本身就有100dp,开发者要求你match。
2.AT_MOST表示:view最多能用100dp,原因可能是开发者让你wrap,而viewGroup最大就100dp.
3.UNSPECIFIED表示:view可以随意,将你最理想的大小告诉viewGroup即可。
代码展现:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
width = MeasureSpec.getMode(widthMeasureSpec);
widthSize = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getMode(heightMeasureSpec);
heightSize = MeasureSpec.getMode(heightMeasureSpec);
setMeasuredDimension(width,height);
}
还有点比较特殊的点:如果view本身并不满意ViewGroup的大小设置,有没有办法协商一下呢?答案是:有。
我可以遵守要求的情况下,同时告诉ViewGroup,虽然我告诉你的我要求的尺寸是遵照你的旨意来的,但实际上我是委屈求全的,我真实想要的大小不是这样的,你能不能再考虑一下。当然ViewGroup会不会理你,那就不一定了。-_-
那就是用这个方法:
resolveSizeAndState((int)(wantedWidth), widthMeasureSpec, 0),
resolveSizeAndState((int) (wantedHeight), heightMeasureSpec, 0);
具体代码:
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
final int result;
switch (specMode) {
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;//在子View想要的空间太大时设置的标记了
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
注:当然一般的viewGroup并没有理睬view的标记,如果自己来自定义的viewGroup倒是可以关注一下旗下子view的诉求啊。^_^
小要点
上面的onMeasure方法中最后多加了行代码,setMeasuredDimension(width,height);
想必很多人注意到了,这是为什么呢?好奇驱动下,我去瞄了瞄源码,贴出来欣赏一下:
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
其中的isLayoutModeOptical方法,源码解释是 “Return true if o is a ViewGroup that is laying out using optical bounds.”大概意思是如果viewGroup是一个可见的布局就返回true。这是对viewGroup是否可见状态子view的大小处理,最终都setMeasuredDimension,将宽和高告诉了ViewGroup。
总的来说,setMeasuredDimension这个方法,就是将view确定好的大小告知ViewGroup,但是这里又有一个问题,如果view在接收到viewGroup对自己的大小安排之后,并没有进行强制改变,再将这个大小传给viewGroup,貌似并没有什么意义,那么什么情况下我们才来充写onMeasure方法,不使用super方法呢?
如果我们接受viewgroup的安排,并没有必要重写onMeasure方法,那有同学不高兴了,如果我其他地方要通过width和height来onDraw其他控件呢,孩纸,你可以用getMeasuredHeight()和getMeasuredWidth()来获得呀,获得的大小就是viewGroup要求你这样干的。晕了不,贴代码看看:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
widthMode = MeasureSpec.getMode(widthMeasureSpec);
widthSize = MeasureSpec.getSize(widthMeasureSpec);//540这是viewGroup告诉你可以最大是540(这是由于布局设置的自适应)
heightMode = MeasureSpec.getMode(heightMeasureSpec);
heightSize = MeasureSpec.getMode(heightMeasureSpec);
Log.d("dongmj","width0:"+getMeasuredWidth()+";"+"height0:"+getMeasuredHeight());// width0:0;height0:0
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {//判断开发者在布局中设置的是否是wrap自适应
widthSize = 20;
heightSize = 20;
}
setMeasuredDimension(widthSize, heightSize);
Log.d("dongmj","width1:"+getMeasuredWidth()+";"+"height1:"+getMeasuredHeight());
}
log打印结果:
09-09 15:34:29.163 1986-1986/com.example.customizeview D/dongmj: width0:0;height0:0
09-09 15:36:42.633 1986-1986/com.example.customizeview D/dongmj: width1:20;height1:20
我在布局里设置的宽和高都是wrap自适应。