实现自定义ViewGroup的一个精简例子

此自定义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);
}
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值