自定义view流式布局FlowLayout

效果图:


image.png
package com.zsw.mycustomviewlearn.customview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zsw
 * @date 2022/4/2
 * @desc
 */
public class FlowLayout extends ViewGroup {
    private List> allViewList = new ArrayList<>();
    private List mLineMaxHeight = new ArrayList<>();

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

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

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

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        super.generateLayoutParams(p);
        return new MarginLayoutParams(p);
    }

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

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    /**
     * 摆放控件 子控件排布
     *
     * @param b  表示该ViewGroup的大小或者位置是否发生变化
     *           以下参数控件的位置
     * @param i
     * @param i1
     * @param i2
     * @param i3
     */
    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        allViewList.clear();
        mLineMaxHeight.clear();
        //每一行View的集合
        List lineViewList = new ArrayList<>();
        //子View的个数
        int childCount = getChildCount();
        //记录每一行的宽度
        int lineWidth = 0;
        //记录每一行子View最高的高度
        int maxLineHeight = 0;
        /*************遍历所有View,将View添加到List>集合中***************/
        for (int j = 0; j < childCount; j++) {
            View childView = getChildAt(j);
            MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
            int childWidth = lp.leftMargin + childView.getMeasuredWidth() + lp.rightMargin;
            int childHeight = lp.topMargin + childView.getMeasuredHeight() + lp.bottomMargin;

            //如果当前这行的已用宽度+这个子view的宽度 大于 父view的宽度那说明折行放不下了需要另外起一行
            if (lineWidth + childWidth > getWidth()) {
                //把这一行的View和高信息加入到集合中
                allViewList.add(lineViewList);
                mLineMaxHeight.add(maxLineHeight);
                //另起一行
                lineWidth = 0;
                maxLineHeight = 0;
                lineViewList = new ArrayList<>();
            }
            lineWidth += childWidth;
            lineViewList.add(childView);
            maxLineHeight = Math.max(maxLineHeight, childHeight);

            //单独处理最后一行
            if (j == childCount - 1) {
                allViewList.add(lineViewList);
                mLineMaxHeight.add(maxLineHeight);
            }

        }
        /************遍历集合中的所有View并显示出来*****************/
        //表示一个view距离父容器的左边距
        int mLeft = 0;
        //表示一个view距离父容器的上边距
        int mTop = 0;
        for (int j = 0; j < allViewList.size(); j++) {
            List lineViews = allViewList.get(j);
            int lineHeight = mLineMaxHeight.get(j);

            for (int k = 0; k < lineViews.size(); k++) {

                View childView = lineViews.get(k);
                MarginLayoutParams mlp = (MarginLayoutParams) childView.getLayoutParams();
                int childLeft = mLeft + mlp.leftMargin;
                int childTop = mTop + mlp.topMargin;
                int childRight = childLeft + childView.getMeasuredWidth();
                int childBottom = childTop + childView.getMeasuredHeight();
                //四个参数分别表示View的左上角和右下角 每个子View放在对应的位置
                childView.layout(childLeft, childTop, childRight, childBottom);
                mLeft += mlp.leftMargin + childView.getMeasuredWidth() + mlp.rightMargin;
            }
            mLeft = 0;
            mTop += lineHeight;
        }

    }

    /**
     * 测量大小
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获得宽高的测量模式和测量值
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //获得容器中子View的个数
        int childCount = getChildCount();
        //记录每一行View的总宽度
        int lineWidth = 0;
        //记录每一行最高View的高度
        int lineHeightMax = 0;
        //记录当前ViewGroup的总高度
        int totalHeight = 0;

        //对子View进行测量
        for (int i = 0; i < childCount; i++) {
            //获得子view
            View childView = getChildAt(i);
            //测量子View
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            //获取子View margin信息
            MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
            //获得子View的测量宽度
            int childWidth = childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            //获得子View的测量高度
            int childHeight = childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            //如果当前这行的已用宽度+这个子view的宽度 大于 父view的宽度那说明折行放不下了需要另外起一行
            if (lineWidth + childWidth > widthSize) {
                //统计总高度
                totalHeight += lineHeightMax;
                //开启新的一行
                lineWidth = childWidth;
                //新的一行的高度
                lineHeightMax = childHeight;
            } else {
                //记录每一行的总宽度
                lineWidth += childWidth;
                //比较每一行最高的View
                lineHeightMax = Math.max(lineHeightMax, childHeight);
            }
            //当该View已是最后一个View时,将该行最大高度添加到totalHeight中
            // (因为上面的代码如果是最后一个时并没有加上这一行的高度) 只有lineHeightMax 赋值,并没有统计到totalHeight
            if (i == childCount - 1) {
                totalHeight += lineHeightMax;
            }
        }
        //如果高度的测量模式是EXACTLY,则高度用测量值,否则用计算出来的总高度(这时高度的设置为wrap_content)
        int realHeightSize = 0;
        //EXACTLY 表示在 XML 布局文件中宽高使用 match_parent 或者固定大小的宽高;表示使用固定值,否则wrap_content
        if (heightMode == MeasureSpec.EXACTLY) {
            //使用固定值
            realHeightSize = heightSize;
        } else {
            //使用计算出来的值
            realHeightSize = totalHeight;
        }
        //设置当前view的大小
        setMeasuredDimension(widthSize, realHeightSize);
    }
}

使用方式一



        

        

        

        

        


        

        

        

        

        

        


    

使用方式二:

xml中:


activity中:
        flowLayout = findViewById(R.id.flowLayout);
        List stringList = new ArrayList<>();
        stringList.add("盘发发簪");
        stringList.add("茶歇连衣裙");
        stringList.add("亲子装");
        stringList.add("情侣对戒");
        stringList.add("气泡");
        stringList.add("旗袍");
        stringList.add("泡泡机");
        stringList.add("蘑菇屋");
        bindDataView(stringList);

public void bindDataView(List strings) {
        if (flowLayout == null) return;
        flowLayout.removeAllViews();
        for (int i = 0; i < strings.size(); i++) {
            View view = createSingleView(strings.get(i));
            flowLayout.addView(view);
        }
    }

    private View createSingleView(String text) {
        View view = LayoutInflater.from(this).inflate(R.layout.item_flow, flowLayout, false);
        TextView textView = view.findViewById(R.id.tv_content);
        textView.setText(text);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
            }
        });
        return view;
    }

item_flow.xml




    

    

你可能感兴趣的:(自定义view流式布局FlowLayout)