以前学的时候纯手写的流式布局(kotlin语言),肯定有很多的不足,比如代码太臃肿,基本没有封装等,但供学习使用还是不错的,话不多说,直接上代码:

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isEmpty

class FlowLayout:ViewGroup {
    constructor(context: Context):super(context){}
    constructor(context: Context, attrs: AttributeSet?):super(context, attrs){}
    //定义控件之间的间隔
    private val space = 30
    //定义所有的二维数组保存所有的View 方便等会儿layout
    var allViews: MutableList<MutableList<View>>  = mutableListOf()
    //定义数组保存一排的View
    var everyLineViews: MutableList<View> = mutableListOf()
    //定义一个数组保存每一行的最大高度,方便等会儿布局使用
    private var allLineHeightList = mutableListOf<Int>()

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        /**
         * 由于onMeasure()方法的多次调用,多以要把这些变量定义在onMeasure()中,防止出错
         */

        //定义所有行中最长的长度(相当于父容器的宽带)
        var maxWidth = 0
        //定义父容器的长度
        var parentHeight = 0
        //定义父容器的宽度
        var parentWidth = 0
        //定义一个变量记录当前行最大的高度
        var currentWidth = space
        // 定义一个变量记录已经使用了多少宽度
        var currentHeight = space
        //定义一个变量保存所有的高度(相当于父容器的高度)
        var totalHeight = 0
        //重新初始化(由于变量定义在外部)
        allViews = mutableListOf()
        everyLineViews = mutableListOf()

        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        //获取父容器的最大宽高
        val parentMaxWidth = MeasureSpec.getSize(widthMeasureSpec)
        Log.v("zj","父容器最大宽度:${parentMaxWidth}")
        val parentMaxHeight = MeasureSpec.getSize(heightMeasureSpec)//不会用到,但还是写了

        //确定子控件的measureSpec
        for(i in 0 until childCount){
            var child = getChildAt(i)
            //得到每个子控件的布局参数
            val lp = child.layoutParams
            //得到子控件的widthSpec
            var childWidthSpec = getChildMeasureSpec(widthMeasureSpec,2*space,lp.width)
            //得到子控件的heightSpec
            var childHeightSpec = getChildMeasureSpec(heightMeasureSpec,2*space,lp.height)
            //测量每个子控件
            child.measure(childWidthSpec,childHeightSpec)


            /**
             * 流式布局最核心的算法
             */

            //在这一行添加
            if(currentWidth+space+child.measuredWidth <= parentMaxWidth){
                currentWidth+=child.measuredWidth+space
                currentHeight = Math.max(currentHeight,(child.measuredHeight+space))
            }else{    //换到下一行

                //将上一行的添加到保存所有View的数组中
                allViews.add(everyLineViews)
                //重置上每一行的数组  但是不能用清空 ,必须重新分配内存(由于指针问题)
                everyLineViews = mutableListOf()
                //将上一行的高度加到总的高度里面
                totalHeight+=currentHeight
                //将上一行的最大高度存到数组中
                allLineHeightList.add(currentHeight)
                maxWidth = Math.max(maxWidth,currentWidth)
                //进行下一行的初始化
                currentHeight = child.measuredHeight+space
                currentWidth = child.measuredWidth+space
            }
            //添加孩子到数组中
            everyLineViews.add(child)
        }
        //判断是否还有不满一行的控件没有被计算到
        if(everyLineViews.size!=0){
            currentWidth = 0
            currentHeight = 0
            for (i in 0 until everyLineViews.size){
                //得到每个子控件
                val child = everyLineViews[i] //这个bug时候来修改过来的,解决最后一行高低总是不对的问题;错误版本是child = getChild(i)
                //当前宽度等于余下的每个子控件的宽度加总 再加上 space间隔
                currentWidth += child.measuredWidth+space
                //当前这一行的高度就是这一个控件的高度+space与上一次保存的最大行高相比,谁大取谁
                currentHeight = Math.max(currentHeight,child.measuredHeight+space)
            }
            //余下的这一行的总宽度再与前面的n行的最长的比较,谁大取谁
            maxWidth = Math.max(maxWidth,currentWidth)
            //算出总的高度
            totalHeight+=currentHeight
            allLineHeightList.add(currentHeight)
            //将这一行添加到数组中
            allViews.add(everyLineViews)
        }
        //设置父容器的最终尺寸
        parentHeight = totalHeight+space
        parentWidth = maxWidth+space
        setMeasuredDimension(parentWidth,parentHeight)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var left = space
        var top = space
        var right = 0
        var bottom = 0
        Log.v("aa","子控件的个数:${childCount}")
        Log.v(("aa"),"添加所有视图有几行:${allViews.size}")
        for (i in 0 until allViews.size){//得到每一行的控件
            //进入第一行的布局
            for(j in 0 until allViews[i].size){//得到每一行中的每个控件
                val child = allViews[i][j]
                right = left+child.measuredWidth
                bottom = top + child.measuredHeight
                child.layout(left,top,right,bottom)
                left+=child.measuredWidth+space
            }
            //计算下一行的相关位置
            top += allLineHeightList[i]
            left = space
        }
    }
}

代码中注释已经写得很详细了