前言
完全自定义View的尺寸以及布局是与之前的不一样的,我们来总结一下
全新定制自定义View的尺寸
// 跟修改已有View的尺寸不一样的是:
// 不需要调用super.onMeasure(), 完全自己计算
// onMeasure()中的两个参数是父View传递过来的限制
// 限制的分类:
// 1 UPSPECIFIED 不限制
// 2 AT_MOST 限制上限
// 3 EXACTLY 限制固定值
// 全新定义自定义View尺寸的方式
// 1 重写onMeasure(), 并计算出View的尺寸
// 使用resolveSize()来让子View的计算结果符合父View的限制
定制Layout的内部布局
// ViewGroup的onMeasure()的重写
// 1 调用每一个子View的measure(), 让子View自我测量
// 2 根据子View给出的尺寸, 得到子View的位置, 并保存它们的位置
// 3 根据子View的位置和尺寸计算出自己的尺寸, 并用setMeasuredDimension()保存
注意:子View的onMeasure()的两个参数, 是需要计算的
开发者的要求即xml文件中layout_开头的属性和ViewGroup剩余的空间这两者结合起来计算得到的
// 可用空间的判断方法
EXACTLY和AT_MOST的可用空间就是MeasureSpec的size值
UNSPECIFIED的可用空间无限大
// 关于保存子View位置的说明
// 不是所有的Layout都需要保存子View的位置, 比如LinearLayout就可以直接累加宽度或者高度
// 有时候对于某些子View需要重复测量两次,比如LinearLayout的宽度是wrap_content,其中一个子View是match_parent
// 它会先用自己的MeasureSpec来测量这个子View, 在所有子View都测量完成后,
LinearLayout会把所有宽度是matche_parent的子View单独拎出来,单独测量一次,
这次使用的mode是EXACTLY, 然后size用的是宽度最宽的那个
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int selWidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int selWidthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
LayoutParams lp = childView.getLayoutParams();
switch (lp.width) {
case MATCH_PARENT:
if (selWidthSpecMode == EXACTLY || selWidthSpecMode == AT_MOST) {
childWidthSPec = MeasureSpec.makeMeasureSpec(selWidthSpecSize - userWidth, EXACTLY);
} else {
childWidthSPec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
break;
case WRAP_CONTENT:
if (selWidthSpecMode == EXACTLY || selWidthSpecMode == AT_MOST) {
childWidthSPec = MeasureSpec.makeMeasureSpec(selWidthSpecSize - userWidth, AT_MOST);
} else {
childWidthSPec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
break;
// 如果是确定的宽度, mode为EXACTLY
default:
childWidthSPec = MeasureSpec.makeMeasureSpec(lp.width, EXACTLY);
}
}
}
@Override
protected void onLayout(boolean b, int i0, int i1, int i2, int i3) {
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
// 在父View的左上右下四个坐标
childView.layout(childLeft[i], chlldTop[i], chlldRight[i], chlldBottom[i]);
}
}
总结
这一节的重点大体就是MeasureSpec的mode, onMeasure(), onLayout()
感觉这节内容好乱,等我回过头来再补充一下吧
贴个HenCoder的链接: