* 1 实现3个构造函数
* 2 重写onMeasure 方法,计算子控件和自己的各种尺寸 (必须调用measureChildren)
* 3 调用onLayout方法,对子位置进行计算,进行布局
*
* 重写方法generateLayoutParams返回MarginLayoutParams 因为可以得到控件的margin参数
public class CustomImgContainer extends ViewGroup
{
private static final String TAG = "CustomImgContainer";
public CustomImgContainer(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public CustomImgContainer(Context context)
{
super(context);
}
public CustomImgContainer(Context context, AttributeSet attrs)
{
super(context, attrs);
}
/**
* 计算所有ChildView的宽度和高度 然后根据ChildView的计算结果,设置自己的宽和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
/**
* 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
Log.e(TAG, (heightMode == MeasureSpec.UNSPECIFIED) + "," + sizeHeight
+ "," + getLayoutParams().height);
// 计算出所有的childView的宽和高
measureChildren(widthMeasureSpec, heightMeasureSpec);
/**
* 记录如果是wrap_content是设置的宽和高
*/
int width = 0;
int height = 0;
int cCount = getChildCount();//得到子容器的数量
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams cParams = null;
// 用于计算左边两个childView的高度
int lHeight = 0;
// 用于计算右边两个childView的高度,最终高度取二者之间大值
int rHeight = 0;
// 用于计算上边两个childView的宽度
int tWidth = 0;
// 用于计算下面两个childiew的宽度,最终宽度取二者之间大值
int bWidth = 0;
/**
* 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时
*/
for (int i = 0; i < cCount; i++)
{
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
cParams = (MarginLayoutParams) childView.getLayoutParams();
// 上面两个childView 的两个宽度+总的Margin(下面相同)
if (i == 0 || i == 1)
{
tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
}
if (i == 2 || i == 3)//下面两个childView
{
bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
}
//左边两个
if (i == 0 || i == 2)
{
lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
}
//右边两个
if (i == 1 || i == 3)
{
rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
}
}
width = Math.max(tWidth, bWidth); //上面两个 和下面两个 宽比较,用最大的
height = Math.max(lHeight, rHeight);//左边 和右边两个 宽比较,用最大的
/**
* 如果是wrap_content设置为我们计算的值
* 否则:直接设置为父容器计算的值
* //设置该ViewGroup的大小
* 如果是 EXACTLY 模式(父容器能完全确定此容器的宽高),则 父容器给当前容器的大小为真正大小
* 否则(wrat_content)按照自己计算的宽高 给这个容器使用
*/
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth
: width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
: height);
}
// abstract method in viewgroup
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int cCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams cParams = null;
/**
* 遍历所有childView根据其宽和高,以及margin进行布局
*/
for (int i = 0; i < cCount; i++)
{
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
cParams = (MarginLayoutParams) childView.getLayoutParams();
int cl = 0, ct = 0, cr = 0, cb = 0;
switch (i)
{
case 0:
cl = cParams.leftMargin;
ct = cParams.topMargin;
break;
case 1:
cl = getWidth() - cWidth - cParams.rightMargin;
ct = cParams.topMargin;
break;
case 2:
cl = cParams.leftMargin;
ct = getHeight() - cHeight - cParams.bottomMargin;
break;
case 3:
cl = getWidth() - cWidth - cParams.rightMargin;
ct = getHeight() - cHeight - cParams.bottomMargin;
break;
}
cr = cl + cWidth;
cb = cHeight + ct;
childView.layout(cl, ct, cr, cb);
}
}
/**
* 如果要自定义ViewGroup支持子控件的layout_margin参数,则自定义的ViewGroup类必须重载generateLayoutParams()函数,
* 并且在该函数中返回一个ViewGroup.MarginLayoutParams派生类对象,这样才能使用margin参数。
*/
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams()
{
Log.e(TAG, "generateDefaultLayoutParams");
return new MarginLayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(
ViewGroup.LayoutParams p)
{
Log.e(TAG, "generateLayoutParams p");
return new MarginLayoutParams(p);
}
/*
* if (heightMode == MeasureSpec.UNSPECIFIED)
{
int tmpHeight = 0 ;
LayoutParams lp = getLayoutParams();
if (lp.height == LayoutParams.MATCH_PARENT)
{
Rect outRect = new Rect();
getWindowVisibleDisplayFrame(outRect);
tmpHeight = outRect.height();
}else
{
tmpHeight = getLayoutParams().height ;
}
height = Math.max(height, tmpHeight);
}
*/
}
布局 xml
<com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#AA333333" > <TextView android:layout_width="150dp" android:layout_height="150dp" android:background="#E5ED05" android:gravity="center" android:text="0" android:textColor="#FFFFFF" android:textSize="22sp" android:textStyle="bold" /> <TextView android:layout_width="50dp" android:layout_height="50dp" android:background="#00ff00" android:gravity="center" android:text="1" android:textColor="#FFFFFF" android:textSize="22sp" android:textStyle="bold" /> <TextView android:layout_width="50dp" android:layout_height="50dp" android:background="#ff0000" android:gravity="center" android:text="2" android:textColor="#FFFFFF" android:textSize="22sp" android:textStyle="bold" /> <TextView android:layout_width="150dp" android:layout_height="150dp" android:background="#0000ff" android:gravity="center" android:text="3" android:textColor="#FFFFFF" android:textSize="22sp" android:textStyle="bold" /> </com.example.zhy_custom_viewgroup.CustomImgContainer>
参考: http://blog.csdn.net/lmj623565791/article/details/38339817