自定义ViewGroup

ViewGroup 运行的基本流程大致为:

1) 测量容器尺寸

重写 onMeasure()方法测量容器大小,和自定义组件有所区别的是,在测量容器大小之
前,必须先调用 measureChildren()方法测量所有子组件的大小,不然结果永远为 0。
2) 确定每个子组件的位置
重写 onLayout()方法确定每个子组件的位置(这个其实挺麻烦,也是定义容器的难点部
分),在 onLayout()方法中,调用 View 的 layout()方法确定子组件的位置。
3) 绘制容器
重写 onDraw()方法,其实 ViewGroup 类并没有重写 onDraw()方法,除非有特别的要求,
自定义容器也很少去重写。比如 LinearLayout 重写了该方法用于绘制水平或垂直分割

条,而 FrameLayout 则是重写了 draw()方法,作用其实是一样的。


工作原理:

重写 onMeasure()方法:
重写 ViewGroup 的 onMeasure()方法时,必须先调用 measureChildren()方法测量子组件的尺寸,measureChildren()方法中,循环遍历每一个子组件,如果当前子组件的可见性不为 GONE 也就是没有隐藏则继续调用 measureChild(child,widthMeasureSpec,heightMeasureSpec)方法测量当前子组件 child 的大小,measureChild()方法结合父容器的 MeasureSpec、子组件的 Padding 和 LayoutParams 三个因素 利 用 getChildMeasureSpec() 计算出子组件的尺寸模式和尺寸大小(可以跟踪到getChildMeasureSpec()方法中查看),并调用子组件的 measure()方法进行尺寸测量。measure()方法调用了 onMeasure(widthMeasureSpec,heightMeasureSpec)方法,该方法正是我们重用的用来测量组件尺寸的方法,至此,测量组件尺寸的工作已掌握到开发人员手中。


接下来调用 onLayout()方法定位子组件,以确定子组件的位置和大小,在 onLayout()方法中,我们将调用子组件的 layout()方法,这里要一分为二,如果子组件是一个 View,定位流程到此结束,如果子组件又是一个容器呢?我们进入 layout()方法进行跟踪。如果子组件是一个容器,又会继续调用该容器的 onLayout()方法对孙组件进行定位,所以onLayout()方法也是一个递归的过程。

重写 onLayout()方法:
protected void onLayout(boolean changed,int l,int t,int r,int b),其中,参数 changed 判断是否有新的大小和位
置,l 表示 left,t 表示 top,r 表示 right,b 表示 bottom,后面的 4 个参数表示容器自己相对父容器的位置以及自身的大小,通常情况下,r-l 的值等同于方法 getMeasuredWidth()方法的返回值,b-t 的值等同于 getMeasuredHeight()方法的返回值。在 onLayout()方法中,需要调用 View 的 layout()方法用于定义子组件和子容器的位置,layout()方法的原理如下:
public void layout(int l,int t,int r,int b)


展示:

public class CustomViewGroup extends ViewGroup {
    private TextView textView;

    public CustomViewGroup(Context context) {
        super(context);
    }

    public CustomViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void init(Context context){
        textView = new TextView(context);
        textView.setText("自定义ViewGroup");
        textView.setTextColor(Color.parseColor("#00a0e9"));
        textView.setBackgroundColor(Color.YELLOW);//TextView的背景
        ViewGroup.LayoutParams lp = new LayoutParams(400,400);//textview的布局参数
        addView(textView,lp);

        setBackgroundColor(Color.BLUE);//ViewGroup的背景颜色
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        View childView = getChildAt(0);
        childView.layout(50,50,textView.getMeasuredWidth()+50,textView.getMeasuredHeight()+50);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        measureChildren(widthMeasureSpec,heightMeasureSpec);//测量子View

        setMeasuredDimension(600,600);//测量ViewGroup的宽高
    }
}

结果显示:



public class CustomViewGroup2 extends ViewGroup {
    public CustomViewGroup2(Context context) {
        super(context);
    }

    public CustomViewGroup2(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomViewGroup2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //测量所有的子View
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        //测量自己的宽高
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);

        setMeasuredDimension(width, height);
    }

    private int measureHeight(int widthMeasureSpec) {
        //父控件建议自己测量的值
        int measureMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureSize = MeasureSpec.getSize(widthMeasureSpec);

        int width = 0;

        if (measureMode == MeasureSpec.EXACTLY) {
            width = measureSize;
        } else if (measureMode == MeasureSpec.AT_MOST) {
            int aWidth = 0;
            int bWidth = 0;
            int cWidth = 0;
            int dWidth = 0;

            for (int i = 0; i < getChildCount(); i++) {
                if (i == 0) {
                    aWidth = getChildAt(i).getMeasuredWidth();//左边View的宽度
                } else if (i == 1) {
                    bWidth = getChildAt(i).getMeasuredWidth();
                } else if (i == 2) {
                    cWidth = getChildAt(i).getMeasuredWidth();
                } else if (i == 3) {
                    dWidth = getChildAt(i).getMeasuredWidth();
                }
            }
            width = Math.max(aWidth, bWidth) + Math.max(cWidth, dWidth);//取到宽度的最大值
        }
        return width;

    }

    private int measureWidth(int heightMeasureSpec) {
        int measureMode = MeasureSpec.getMode(heightMeasureSpec);
        int measureSize = MeasureSpec.getSize(heightMeasureSpec);

        int height = 0;

        if (measureMode == MeasureSpec.EXACTLY) {
            height = measureSize;
        } else if (measureMode == MeasureSpec.AT_MOST) {
            int aHeight = 0;
            int bHeight = 0;
            int cHeight = 0;
            int dHeight = 0;

            for (int i = 0; i < getChildCount(); i++) {
                if (i == 0) {
                    aHeight = getChildAt(i).getMeasuredHeight();
                } else if (i == 1) {
                    bHeight = getChildAt(i).getMeasuredHeight();
                } else if (i == 2) {
                    cHeight = getChildAt(i).getMeasuredHeight();
                } else if (i == 3) {
                    dHeight = getChildAt(i).getMeasuredHeight();
                }
            }
            height = Math.max(aHeight, cHeight) + Math.max(bHeight, dHeight);//取到宽度的最大值
        }
        return height;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++) {
            View childView = getChildAt(i);//某一个子View

            if (i == 0) {
                childView.layout(0,0,childView.getMeasuredWidth(),childView.getMeasuredHeight());
            } else if (i == 1) {
                childView.layout(getMeasuredWidth()-childView.getMeasuredWidth(),0,getMeasuredWidth(),childView.getMeasuredHeight()+0);
            } else if (i == 2) {
                childView.layout(0,getMeasuredHeight()-childView.getMeasuredHeight(),childView.getMeasuredWidth(),getMeasuredHeight()+0);
            } else if (i == 3) {
                childView.layout(getMeasuredWidth()-childView.getMeasuredWidth(),getMeasuredHeight()-childView.getMeasuredHeight(),getMeasuredWidth(),getMeasuredHeight());
            }
        }
    }
}


CustomViewGroup2的布局:

<com.example.user.myapplication4.CustomViewGroup2
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_blue_bright" />

    <TextView
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:background="@android:color/holo_blue_dark" />

    <TextView
        android:layout_width="140dp"
        android:layout_height="150dp"
        android:background="@android:color/holo_red_dark" />

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/holo_green_light" />
</com.example.user.myapplication4.CustomViewGroup2>


结果显示:


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值