今天倒腾一了一下自定义直接继承自ViewGroup的自定义容器,结果什么也没倒腾出来,不过导师收获到了不少,在这里总结一下,怕到时候忘掉了。
1.我们都知道ViewGroup中的LayoutParams是不带有Margin值的,所以刚开始的时候想着继承自ViewGroup.LayoutParams写一个LayoutParams,把改加的都加上,但是这里产生了一个误区,我一直认为LayoutParams这个类是给自己调用的,然后获取自己的Margin值设置进去;但是翻看了一遍ViewGroup和LinearLayout的源码,发现并没有在类中的成员变量存有LayoutParams的引用,却是在addView()方法中调用了LayoutParams的构造方法;联想到项目中经常发生的LayoutParams强转错误的案例,搞懂了为什么,容器类中的LayoutParams都是给子类去设置的,并不是给自己用的;
2.LinearLayout的LayoutParams继承的并不是ViewGroup.LayoutParams而是ViewGroup中的另外一个MarginLayoutParams,而MarginLayoutParams是直接继承ViewGroup.LayoutParams的,MarginLayoutParams中保存了margin值。
3.View中是没有LayoutParams的,所以TextView等View控件中的LayoutParams都是由父容器设置的,也证实了第一点,源码如下:
if (!checkLayoutParams(params)) {
params = generateLayoutParams(params);
}
这个是ViewGroup中的一段代码;通过这段代码将LayoutParams设置到View上去;所以我们还需要重写generateLayoutParams()这个方法,否则在自定义容器中调用child.getLayoutParams()得到的是ViewGroup.LayoutParams,不能获取到自己定义的,强转将会报错,
代码如下:
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof LayoutParams) {
return new LayoutParams((LayoutParams) lp);
} else if (lp instanceof MarginLayoutParams) {
return new LayoutParams((MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
}
4.当自定义容器的属性为layout_width或者layout_height属性为WRAP_CONTENT时,需要自己通过setMeasureDimension(width,height)方法手动设置宽高,否则默认设置的宽高是0。
5.当容器内的子View的Layout_width或者layout_height属性为MATCH_PARENT时,需要重新测量View的宽高,否则MATCH_PARENT属性的效果无效而变为WRAP_CONTENT的效果。
onMeasure代码如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
List<View> list = new ArrayList<>();
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int mHeight = 0;
int childCount = getChildCount();
switch (heightMode) {
case MeasureSpec.UNSPECIFIED:
break;
case MeasureSpec.AT_MOST:
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
if (params.height == ViewGroup.LayoutParams.MATCH_PARENT){
list.add(child);
}else if (params.height == ViewGroup.LayoutParams.WRAP_CONTENT){
mHeight = Math.max(mHeight, child.getMeasuredHeight() + params.bottomMargin + params.topMargin);
}else if (params.height > 0){
mHeight = Math.max(mHeight, child.getMeasuredHeight() + params.bottomMargin + params.topMargin);
}
}
if (list.size() != 0){
int realHeightSpec;
for (int i = 0; i < list.size(); i++) {
View child = list.get(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
if (child.getMeasuredHeight() < (mHeight - params.bottomMargin - params.topMargin)){
realHeightSpec = MeasureSpec.makeMeasureSpec(mHeight - params.bottomMargin - params.topMargin,MeasureSpec.EXACTLY);
measureChild(child,widthMeasureSpec,realHeightSpec);
}
}
}
break;
case MeasureSpec.EXACTLY:
MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
mHeight = getMeasuredHeight() + params.topMargin + params.bottomMargin;
break;
}
setMeasuredDimension(getMeasuredWidth(), mHeight);
}