自定义ViewGroup

来源:互联网 发布:北外网络教育作业答案 编辑:程序博客网 时间:2024/05/20 05:25

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