自定义控件系列:
秒懂OnMeasure
秒懂OnLayout
让自定义ViewGroup里的子控件支持Margin
让自定义ViewGroup支持Padding
自定义ViewGroup的一个综合实践 FlowLayout
知识点:
- 让自定义ViewGroup里的子控件支持Margin,需要自定义ViewGroup在自身的onMeasure和onLayout里去处理Margin,
因为子控件写margin之后,必然会让自定义ViewGroup的宽高受到影响(所以onMeasure需要处理)
子控件写margin之后,自定义ViewGroup在摆放这些子控件时候,必须考虑到margin,才能摆放到正确的位置所以onLayout需要处理)
- 但是如何得到子控件的Margin呢,不是你想的那么容易直接就可拿到的,必须重写下面的三个方法,但是好在写法固定,照抄即可,想知道为什么,网上搜索吧,但是我觉得这是一个结论直接记住就行了,哪有那么多为什么(点到为止)
// 支持Margin的固定写法,下面照抄就行了,至于为什么,可以去看源码,但是我觉得直接记住就ok了
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
应用
1. 继续写上文的MyLinearLayout,让他的子控件支持Margin
先看疗效:

第一步:支持Margin的固定写法
// 支持Margin的固定写法,下面照抄就行了,至于为什么,可以去看源码,但是我觉得直接记住就ok了
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
第二步:onMeasure里考虑margin

private Point caculateAtMostSize(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
// 测量一下子控件的宽高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到MarginLayoutParams,margin就在这里保存着
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 获得子控件的宽高(需要加上对应的margin,让控件的宽高包含margin,
// 这样才能让自定义的viewgroup在计算自身在AtMost模式的尺寸时候考虑到这些margin)
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
// 因为我们的自定义View模拟的是竖向的LinearLayout,所以:
// 控件的宽度为所有子控件里,宽度最大的那个view的宽度,
// 控件高度是所有子空间的高度之和
width = Math.max(childWidth, width);
height += childHeight;
}
return new Point(width, height);
}
第三步:onLayout里考虑Margin

代码如下:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int top = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
// 得到MarginLayoutParams,margin就在这里保存着
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 获得子控件的宽高(需要加上对应的margin,让控件的宽高包含margin)
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
// 布局,就是设置好这个子控件的左上右下
// 在布局的时候,考虑控件的margin,把子view摆放好
// (这是一个竖向的LinearLayout,想想如何布置子控件)
child.layout(lp.leftMargin,
top + lp.topMargin,
childWidth - lp.rightMargin,
top + childHeight - lp.bottomMargin);
top += childHeight;
}
}
2946



被折叠的 条评论
为什么被折叠?



