Android自定义ViewGruop

上一篇我们了解了自定义View的过程,如果不了解请阅读http://blog.csdn.net/caihongdao123/article/details/51690285。今天我们来了解一下自定义ViewGroup!


自定义ViewGroup其实跟自定义View类似,只不过它更加复杂一点,因为它是个容器,所以不仅要管好自己,还要管理好容器中的每个子控件(子控件的位置、间距等)。
举个栗子:我们就以大家都熟悉的LinearLayout来作为模板,LinearLayout大家都知道可以横向显示页可以纵向显示控件。
首先我们自定义ViewGroup也必须重写onMeasure,实现测量子View大小以及设定ViewGroup的大小,这个测量比起自定义View会相对复杂一点:
1.我们要考虑宽度最大子View的值,然后我们ViewGroup的值肯定是要大于或等于这个值才能够完全显示所有子控件的宽度。
2.我们要计算出所有子View的高度和,原因同上。
下面直接看代码,注释中有比较详细的解说:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //将所有的子View进行测量,这会触发每个子View的onMeasure函数(注意要与measureChild区分,measureChild是对单个view进行测量)
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int childCount = getChildCount();//获取子View数量
        if (childCount == 0) {
            setMeasuredDimension(0, 0);//如果没有子View,当前ViewGroup没有存在的意义,所以设置宽高都是0,节约空间
        } else {
            //如果宽高都是包裹内容
            if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
                //我们将高度设置为所有子View的高度相加,宽度设为子View中最大的宽度
                int height = getTotleHeight();
                int width = getMaxChildWidth();
                setMeasuredDimension(width, height);

            } else if (heightMode == MeasureSpec.AT_MOST) {//如果只有高度是包裹内容
                //宽度设置为ViewGroup自己的测量宽度,高度设置为所有子View的高度总和
                setMeasuredDimension(widthSize, getTotleHeight());
            } else if (widthMode == MeasureSpec.AT_MOST) {//如果只有宽度是包裹内容
                //宽度设置为子View中宽度最大的值,高度设置为ViewGroup自己的测量值
                setMeasuredDimension(getMaxChildWidth(), heightSize);

            }
        }
    }
    /***
     * 获取子View中宽度最大的值
     */
    private int getMaxChildWidth() {
        int childCount = getChildCount();
        int maxWidth = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView.getMeasuredWidth() > maxWidth)
                maxWidth = childView.getMeasuredWidth();

        }

        return maxWidth;
    }

    /***
     * 将所有子View的高度相加
     **/
    private int getTotleHeight() {
        int childCount = getChildCount();
        int height = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            height += childView.getMeasuredHeight();

        }
		return height;
	}


与自定义View不同,我们接下来实现的不是OnDraw()方法,而是onLayout()方法将测量好的控件摆放到ViewGroup中,看代码:

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        //记录当前的高度位置
        int curHeight = t;
        //将子View逐个摆放
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int height = child.getMeasuredHeight();
            int width = child.getMeasuredWidth();
            //摆放子View,参数分别是子View矩形区域的左、上、右、下
            child.layout(l, curHeight, l + width, curHeight + height);
            curHeight += height;
        }
    }
ok,搞定!我们来测试一下我们的ViewGroup,看看是不是跟LinearLayout效果一样一样!
将我们自定义的ViewGroup里面放3个TextView ,并且宽度设置不一样,ViewGroup的宽高都设置wrap_content,为了效果明显给自定义ViewGroup加个背景,给最长的View加了个背景

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.ljw.MyViewGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#EEEEEE">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:text="90dp" />

        <TextView
            android:layout_width="160dp"
            android:layout_height="wrap_content"
            android:text="160dp" 
	    android:background="#5d5d5d"/>

        <TextView
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:text="30dp" />
    </com.hc.studyview.MyViewGroup>
</LinearLayout>
看下效果图,是不是跟LinearLayout一样一样的


把MyViewGroup代码贴出来:
/**
 * @Arthur Jovial  比较喜欢丶笑
 * 2016-06-16
 */
public class MyViewGroup extends ViewGroup {
    public MyViewGroup(Context context) {
        super(context);
    }

    public MyViewGroup(Context context, AttributeSet attrs) {

        super(context, attrs);
    }

    /***
     * 获取子View中宽度最大的值
     */
    private int getMaxChildWidth() {
        int childCount = getChildCount();
        int maxWidth = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView.getMeasuredWidth() > maxWidth)
                maxWidth = childView.getMeasuredWidth();

        }

        return maxWidth;
    }

    /***
     * 将所有子View的高度相加
     **/
    private int getTotleHeight() {
        int childCount = getChildCount();
        int height = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            height += childView.getMeasuredHeight();

        }
        return height;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //将所有的子View进行测量,这会触发每个子View的onMeasure函数(注意要与measureChild区分,measureChild是对单个view进行测量)
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int childCount = getChildCount();//获取子view数量

        if (childCount == 0) {
            setMeasuredDimension(0, 0);//如果没有子View,当前ViewGroup没有存在的意义,所以设置宽高都是0,节约空间
        } else {
            //如果宽高都是包裹内容
            if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
                //我们将高度设置为所有子View的高度相加,宽度设为子View中最大的宽度
                int height = getTotleHeight();
                int width = getMaxChildWidth();
                setMeasuredDimension(width, height);

            } else if (heightMode == MeasureSpec.AT_MOST) {//如果只有高度是包裹内容
                //宽度设置为ViewGroup自己的测量宽度,高度设置为所有子View的高度总和
                setMeasuredDimension(widthSize, getTotleHeight());
            } else if (widthMode == MeasureSpec.AT_MOST) {//如果只有宽度是包裹内容
                //宽度设置为子View中宽度最大的值,高度设置为ViewGroup自己的测量值
                setMeasuredDimension(getMaxChildWidth(), heightSize);

            }
        }
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        //记录当前的高度位置
        int curHeight = t;
		//将子View逐个摆放
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int height = child.getMeasuredHeight();
            int width = child.getMeasuredWidth();
            //摆放子View,参数分别是子View矩形区域的左、上、右、下
            child.layout(l, curHeight, l + width, curHeight + height);
            curHeight += height;
        }
    }
}
总结:自定义ViewGroup也讲完了,应该都有个基本的认识了,其实归结起来自定义View或者ViewGroup都是重写那几个方法,只是view和ViewGroup重写的方法有些不同,毕竟一个是控件,一个是容器!

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值