ViewGroup设置margin累计分为三步:
1.获取margin
2.onMeasure里面加上margin
3.onLayout布局设置margin
获取margin
首先呢,ViewGroup是自带的MarginLayoutParams的,但是在addView时,查看源码:
public void addView(View child, int index) {
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException(
"generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}
然后点开generateDefaultLayoutParams,很明显,这个generateDefaultLayoutParams是不支持MarginLayoutParams的,但是我们需要获取到margin值,就需要自己来重写这个generateDefaultLayoutParams。
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
重写的generateDefaultLayoutParams,为了兼容xml和代码new出来addView.这里我们复写了所有构造。
public static class VerticalLayoutParams extends MarginLayoutParams {
public VerticalLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public VerticalLayoutParams(int width, int height) {
super(width, height);
}
public VerticalLayoutParams(LayoutParams lp) {
super(lp);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new VerticalLayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
return new VerticalLayoutParams(lp);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new VerticalLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
好了,准备工作已经做完了,接下来就可以来计算高度了,首先我们用手动的方式来计算一下总高度:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int totalHeight = 0;
int totalWidth = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
//计算总共高度
totalHeight = child.getMeasuredHeight() + totalHeight;
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
totalHeight = totalHeight+((ViewGroup.MarginLayoutParams)layoutParams).topMargin;
//取最大的子view的宽度
totalWidth = Math.max(child.getMeasuredWidth(), totalWidth);
}
float xmlMaxWidth = getResources().getDimension(R.dimen.dp250);
float xmlMaxHeight = getResources().getDimension(R.dimen.dp100);
Log.d(TAG,"xmlMaxWidth = "+xmlMaxWidth+" xmlMaxHeight = "+xmlMaxHeight+" totalWidth = "+(totalWidth)+" totalHeight = "+totalHeight);
setMeasuredDimension(totalWidth,totalHeight);
}
运行试验结果:
线面空出来了一部分黑色。这是因为我们的layout并未将margin这部分实现,那么接下来就来实现一下吧:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int curTop = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
VerticalLayoutParams layoutParams = (VerticalLayoutParams) child.getLayoutParams();
curTop = curTop+layoutParams.topMargin;
child.layout(0,curTop,child.getMeasuredWidth(),curTop+child.getMeasuredHeight());
curTop = curTop + child.getMeasuredHeight();
}
}
这短代码仅仅增加了:
VerticalLayoutParams layoutParams = (VerticalLayoutParams) child.getLayoutParams();
curTop = curTop+layoutParams.topMargin;
获取margin并累加,看实验结果:
已经实现了正常的margin逻辑。