自定义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<List<View>> allViewList = new ArrayList<>();
    private List<Integer> 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<View> lineViewList = new ArrayList<>();
        //子View的个数
        int childCount = getChildCount();
        //记录每一行的宽度
        int lineWidth = 0;
        //记录每一行子View最高的高度
        int maxLineHeight = 0;
        /*************遍历所有View,将View添加到List<List<View>>集合中***************/
        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<View> 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);
    }
}

使用方式一

<com.zsw.mycustomviewlearn.customview.FlowLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="150dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="#00ff00">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="aaaaaaaaaa" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="bbbbbbb" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="CCCCCCCCC" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="rrrrrrrr" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="eeee" />


        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="vvvvvvv" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="ttt" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="666666666666" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="uuuuuuuuuu" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="qqqqqqqqqq" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:layout_marginBottom="10dp"
            android:background="#ff4000"
            android:text="eeeeeeeeeeee" />


    </com.zsw.mycustomviewlearn.customview.FlowLayout>

使用方式二:

xml中:
<com.zsw.mycustomviewlearn.customview.FlowLayout
        android:id="@+id/flowLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

activity中:
        flowLayout = findViewById(R.id.flowLayout);
        List<String> 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<String> 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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="30dp"
    android:background="@drawable/bg_item_flow"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="5dp"
    android:layout_marginBottom="10dp"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="优惠券"
        android:textColor="#333333"
        android:textSize="14dp"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="10dp"/>

    <ImageView
        android:layout_width="22dp"
        android:layout_height="22dp"
        android:layout_marginLeft="10dp"
        android:src="@drawable/xiaolian"/>
</LinearLayout>
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容