欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android自定义ViewGroup控件

程序员文章站 2022-06-08 17:01:55
...

一、简单实现自定义ViewGroup四个顶点放置View
重写generateLayoutParams方法指定ViewGroup的LayoutParams为MarginLayoutParams。重写onMeasure方法,计算childView的测量模式及值,设置ViewGroup的宽和高。重写onLayout方法,设置所有childView的位置。

public class NewlineViewGroup extends ViewGroup {

    private MarginLayoutParams params = null;
    private int count;
    private int cWidth = 0;
    private int cHeight = 0;

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

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

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

    /**
     * 重写父类的该方法,返回MarginLayoutParams的实例
     *
     * @param attrs
     * @return
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //计算出所有的childView的宽高
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        //获取childView数量
        count = getChildCount();
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    /**
     * 测量宽度
     *
     * @param measureSpec
     * @return
     */
    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.AT_MOST) {//设置为wrap_content
            int t = 0, b = 0;
            for (int i = 0; i < count; i++) {
                View childView = getChildAt(i);
                //获取子view的宽
                cWidth = childView.getMeasuredWidth();
                params = (MarginLayoutParams) childView.getLayoutParams();
                if (i == 0 || i == 1) {
                    t += cWidth + params.leftMargin + params.rightMargin;
                }
                if (i == 2 || i == 3) {
                    b += cWidth + params.leftMargin + params.rightMargin;
                }
            }
            specSize = Math.max(t, b);
            return specSize;
        }
        return specSize;
    }

    /**
     * 测量高度
     *
     * @param measureSpec
     * @return
     */
    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.AT_MOST) {//设置为wrap_content
            int l = 0, r = 0;
            for (int i = 0; i < count; i++) {
                View childView = getChildAt(i);
                //获取子view的高
                cHeight = childView.getMeasuredHeight();
                params = (MarginLayoutParams) childView.getLayoutParams();
                if (i == 0 || i == 2) {
                    l += cHeight + params.topMargin + params.bottomMargin;
                }
                if (i == 1 || i == 3) {
                    r += cHeight + params.topMargin + params.bottomMargin;
                }
            }
            specSize = Math.max(l, r);
            return specSize;
        }
        return specSize;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < count; i++) {
            View childView = getChildAt(i);
            cWidth = childView.getMeasuredWidth();
            cHeight = childView.getMeasuredHeight();
            params = (MarginLayoutParams) childView.getLayoutParams();
            int cl = 0, cr = 0, ct = 0, cb = 0;
            switch (i) {
                case 0:
                    cl = params.leftMargin;
                    ct = params.topMargin;
                    break;
                case 1:
                    cl = getWidth() - cWidth - params.leftMargin;
                    ct = params.topMargin;
                    break;
                case 2:
                    cl = params.leftMargin;
                    ct = getHeight() - cHeight - params.bottomMargin;
                    break;
                case 3:
                    cl = getWidth() - cWidth - params.leftMargin - params.rightMargin;
                    ct = getHeight() - cHeight - params.bottomMargin;
                    break;
            }
            cr = cl + cWidth;
            cb = cHeight + ct;
            childView.layout(cl, ct, cr, cb);
        }
    }
}

xml文件中的实现

    <com.example.yinyuetai.myapplication.newline.NewlineViewGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp">

        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#FF4444"
            android:gravity="center"
            android:text="0"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#00ff00"
            android:gravity="center"
            android:text="1"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#ff0000"
            android:gravity="center"
            android:text="2"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#0000ff"
            android:gravity="center"
            android:text="3"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    </com.example.yinyuetai.myapplication.newline.NewlineViewGroup>

二、实现ViewGroup自动换行
代码中邮详细注解不在赘述

private int childCount;
    private static final int PADDING_HOR = 20;
    private static final int PADDING_VER = 20;
    private static final int SIDE_MARGIN = 40;
    private static final int TEXT_MARGIN = 40;

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

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

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

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //横纵坐标
        int x = 0;
        int y = 0;
        //行数
        int rows = 1;
        //获取view的数量
        childCount = getChildCount();
        //获取父控件宽度
        int specWidth = MeasureSpec.getSize(widthMeasureSpec);
        //实际宽度
        int actualWidth = specWidth - SIDE_MARGIN * 2;
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            //设置边框距离
            view.setPadding(PADDING_HOR, PADDING_VER, PADDING_HOR, PADDING_VER);
            //设置父控件ViewGroup大小
            view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
            //获得当前控件的宽高
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            //计算总宽度
            x += width + TEXT_MARGIN;
            //判断是否超出实际宽度
            if (x > actualWidth) {
                x = width;
                rows++;
            }
            y = rows * (height + TEXT_MARGIN);
        }
        setMeasuredDimension(actualWidth, y);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int actualWidth = r - l;
        //横纵坐标开始
        int x = 0;
        int y = 0;
        //行数
        int rows = 1;
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            x += width + TEXT_MARGIN;
            if (x > actualWidth) {
                x = width + TEXT_MARGIN;
                rows++;
            }
            y = rows * (height + TEXT_MARGIN);
            view.layout(x - width, y - height, x, y);
        }
    }
}