此自定义VIewGroup只做演示用,目前的代码限制其只能包含一个子View,子View可以设置间距、layout_gravity属性,以下分别为我测试用的布局文件和自定义的ViewGroup类的源码:
测试activity的布局文件:
<LinearLayout 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:orientation="vertical">
<com.mytest.OnlyOneChildViewViewGroup
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_marginLeft="30dp"
android:layout_marginTop="100dp"
android:background="#ff0000"
android:paddingLeft="20dp" >
<TextView
android:id="@+id/textViewTest"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_gravity="right|bottom"
android:background="#00ff00"
android:text="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789......"
android:textSize="30sp" />
</com.carter.launcher.OnlyOneChildViewViewGroup>
</LinearLayout>
自定义ViewGroup类的源码:
public class OnlyOneChildViewViewGroup extends ViewGroup {
public OnlyOneChildViewViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
//设置ViewGroup及其子View的合适大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (getChildCount() > 0) {
final View child = getChildAt(0);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int leftPaddingMargin = getPaddingLeft() + lp.leftMargin;
int rightPaddingMargin = getPaddingRight() + lp.rightMargin;
int horizontalSpace = leftPaddingMargin + rightPaddingMargin;
int topPaddingMargin = getPaddingTop() + lp.topMargin;
int bottomPaddingMargin = getPaddingBottom() + lp.bottomMargin;
int verticalSpace = topPaddingMargin + bottomPaddingMargin;
//根据左右边距、左右间距、ViewGroup的宽度参数、子View的宽度参数得到子View在ViewGroup中实际显示宽度
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, horizontalSpace, lp.width);
childWidthMeasureSpec = Math.min(childWidthMeasureSpec, widthMeasureSpec - horizontalSpace);
//根据上下边距、上下间距、ViewGroup的高度参数、子View的高度参数得到子View在ViewGroup中实际显示高度
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, verticalSpace, lp.height);
childHeightMeasureSpec = Math.min(childHeightMeasureSpec, heightMeasureSpec - verticalSpace);
//设置子View在ViewGroup中的显示大小
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (View.GONE != child.getVisibility()) {
//根据ViewGroup布局参数中的宽高及上面子View计算出的大小参数来设置ViewGroup的显示大小
setMeasuredDimension(
Math.max(child.getMeasuredWidth() + horizontalSpace, widthMeasureSpec),
Math.max(child.getMeasuredHeight() + verticalSpace, heightMeasureSpec));
} else {
//子View设置为gone,所以设置ViewGroup的大小为其布局参数确定的大小
setMeasuredDimension(Math.max(horizontalSpace, widthMeasureSpec),
Math.max(verticalSpace, heightMeasureSpec));
}
} else {
//不包含子View时,设置ViewGroup的大小为其布局参数确定的大小
setMeasuredDimension(Math.max(getPaddingLeft() + getPaddingRight(), widthMeasureSpec),
Math.max(getPaddingTop() + getPaddingBottom(), heightMeasureSpec));
}
}
//根据ViewGroup和子View的布局参数,将子View布局在ViewGroup中的合适区域内
@Override
protected void onLayout(boolean arg0, int left, int top, int right, int bottom) {
if (getChildCount() > 0) {
final View child = getChildAt(0);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int leftPaddingMargin = getPaddingLeft() + lp.leftMargin;
int topPaddingMargin = getPaddingTop() + lp.topMargin;
int childLeft;
int childTop;
final int parentLeft = getPaddingLeft();
final int parentRight = right - left - getPaddingRight();
final int parentTop = getPaddingTop();
final int parentBottom = bottom - top - getPaddingBottom();
//获取子View在布局文件中定义的layout_gravity属性
final int horizontalGravity = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
final int verticalGravity = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
//解析layout_gravity属性中水平方向上的值
switch (horizontalGravity) {
case Gravity.LEFT:
childLeft = leftPaddingMargin;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - child.getMeasuredWidth()) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = parentRight - child.getMeasuredWidth() - lp.rightMargin;
break;
default:
childLeft = leftPaddingMargin;
}
//解析layout_gravity属性中垂直方向上的值
switch (verticalGravity) {
case Gravity.TOP:
childTop = topPaddingMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - child.getMeasuredHeight()) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - child.getMeasuredHeight() - lp.bottomMargin;
break;
default:
childTop = topPaddingMargin;
}
//根据layout_gravity属性及间接、边距,将子View显示在ViewGroup中合适的位置区域
child.layout(childLeft, childTop, childLeft+child.getMeasuredWidth(), childTop+child.getMeasuredHeight());
}
}
//应用下面自定义的LayoutParams,而不是ViewGroup.LayoutParams
@Override
public android.view.ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
//应用下面自定义的LayoutParams,而不是ViewGroup.LayoutParams
@Override
protected android.view.ViewGroup.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
//由于要在子View中设置间距和layout_gravity属性,所以这里需要覆写ViewGroup中的LayoutParams类
public static class LayoutParams extends MarginLayoutParams {
public int gravity = -1;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
String attrName = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
attrName = attrs.getAttributeName(i);
//这里是为了取出子View中定义的layout_gravity属性值
if ("layout_gravity".equals(attrName)) {
gravity = Integer.parseInt(new BigInteger(
//得到的返回值是0x1等形式的十六进制字符串,以下方式将此类型字符串转换为十进制整数值
attrs.getAttributeValue(i).replaceFirst("0[x X]", ""), 16).toString());
break;
}
}
}
public LayoutParams(int width, int height) {
this(width, height, -1);
}
public LayoutParams(int width, int height, int gravity) {
super(width, height);
this.gravity = gravity;
}
public LayoutParams(android.view.ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
}