Android 自定义View --- 流式布局FlowLayout

Android 自定义View — 流式布局FlowLayout

1、流式布局的效果

在这里插入图片描述

2、开发准备

FlowLayout是一个容器View,所以我们继承的是ViewGroup,需要重写onMeasure方法和onLayout方法。onMeasure方法主要是测量子View,onLayout方法主要是布局子View。

测量子View的方法是View.measure(int widthMeasureSpec, int heightMeasureSpec),widthMeasureSpec和heightMeasureSpec参数可以通过getChildMeasureSpec(int spec, int padding, int childDimension)方法获取,spec是View的MeasureSpec,可以是width或height;padding是View的内部边距,左右或上下边距的和;childDimension是子View的LayoutParams.width或LayoutParams.height

布局子View的方法是View.layout(int l, int t, int r, int b),l、t、r、b参数由测量结果计算确定。

3、开发

1)定于全局私有变量

private List<List<View>> lineView;    //保存每行子View列表的列表
private int marginX = 0;    //子View间x坐标边距
private int marginY = 0;    //子View间Y坐标边距

2)重写onMeasure方法

这个方法主要是测量个子View大小,并计算排列好子View的位置和保存每一行的子View

	@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        System.out.println("onMeasure");
        lineView = new ArrayList<>();   //初始化子View列表
        int paddingX = getPaddingLeft() + getPaddingRight();     //获取左右内边距尺寸
        int paddingY = getPaddingTop() + getPaddingBottom();     //获取上下内边距尺寸
        int width = MeasureSpec.getSize(widthMeasureSpec) - paddingX - marginX * 2;  //获取子View左右可用尺寸
        int height = MeasureSpec.getSize(heightMeasureSpec) - paddingY - marginY * 2;   //获取子View上下可用尺寸
        System.out.println("width = " + width + " , height = " + height + "  ,  paddingX = " + paddingX + " , paddingY = " + paddingY);

        List<View> list = new ArrayList<>();    //保存一行子View的列表
        lineView.add(list);
        int useWidth = 0;         //一行使用的宽度
        int maxWidth = 0;         //view中被子View使用的最大宽度,即全部行中被使用的最大宽度
        int useHeight = 0;        //每一行中最大使用的高度
        int maxHeight = 0;        //view中被子View使用的最大高度


        //获取子View数量
        int count = getChildCount();
        //遍历
        for (int i = 0; i < count; i++) {
            //获取子View
            View child = getChildAt(i);
            //获取子View布局参数
            LayoutParams layoutParams = child.getLayoutParams();

            int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                    paddingX + marginX * 2, layoutParams.width);
            int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                    paddingY + marginY * 2, layoutParams.height);
            //测量子View
            child.measure(childWidthMeasureSpec,childHeightMeasureSpec);

            //获取子View的测量宽高
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            //判断这一行是否还能存下子View,通过已使用宽度加子View宽度与view的可使用宽度比较
            if((useWidth + childWidth) >= width){
                list = new ArrayList<>();   //创建新的一行子View列表
                lineView.add(list);         //把一行子View列表保存在lineView
                //原来使用宽度的最大值与这一行使用的宽度来取最大值
                maxWidth = Math.max(maxWidth,useWidth);
                //一行结束后,更新使用的高度
                maxHeight = maxHeight + useHeight + marginY;
                //状态置零
                useWidth = 0;
                useHeight = 0;
            }
            list.add(child);   //保存子View
            //更新这一行使用的使用宽度
            useWidth = useWidth + childWidth + marginX;
            //保存这一行使用的最大高度
            useHeight = Math.max(childHeight,useHeight);

        }

        //遍历结束后,可能最后一个子View不是一行的最后一个,所以需要我们判断处理
        if(useWidth != 0 && useHeight != 0){
            maxWidth = Math.max(maxWidth,useWidth);
            maxHeight = maxHeight + useHeight;
        }

        //获取原来View的布局参数
        LayoutParams layoutParams = getLayoutParams();
        //如果使用WRAP_CONTENT,则使用我们根据子View测量的结果
        if(layoutParams.width == LayoutParams.WRAP_CONTENT){
            width = maxWidth;
        }

        //如果使用WRAP_CONTENT,则使用我们根据子View测量的结果
        if(layoutParams.height == LayoutParams.WRAP_CONTENT){
            height = maxHeight;
        }

        //保存测量结果
        setMeasuredDimension(width + paddingX + marginX * 2,
                height +paddingY + marginY * 2);

    }

3)重写onLayou方法

这个方法主要是根据测量的结果,计算布局子View

	@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(lineView != null){
            //子View开始布局的位置
            int useHeight = getPaddingTop() + marginY;
            int useWidth = getPaddingLeft() + marginX;

            for (int i = 0; i < lineView.size(); i++) {
                List<View> list = lineView.get(i);
                if(list != null){
                    int lineUseHeight = 0;    //用来保存每一行使用的最大高度
                    for (int i1 = 0; i1 < list.size(); i1++) {
                        View child = list.get(i1);
                        int childWidth = child.getMeasuredWidth();
                        int childHeight = child.getMeasuredHeight();
//                        System.out.println("childWidth = " + childWidth + " , childHeight = " + childHeight);
                        //布局子View
                        child.layout(useWidth,useHeight,
                                useWidth + childWidth,useHeight + childHeight);
                        //更新已使用的宽度
                        useWidth += marginX + childWidth;
                        //更新这一行已使用的最大高度
                        lineUseHeight = Math.max(lineUseHeight,childHeight);
                    }
                    //布局完一行子View后,需要更新已使用的宽高
                    useWidth = getPaddingLeft() + marginX;
                    useHeight += lineUseHeight + marginY;
                }
            }
            //全部子View布局完后,清除子View的保存
            lineView.clear();
        }
    }
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

szzyjsxyzwy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值