kolint版本流式布局FlowLayout

流式布局FlawLayout自定义view
package com.example.lsn_compose.view

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import com.blankj.utilcode.util.ConvertUtils.dp2px
import kotlin.math.max

class FlowLayout(context: Context) : ViewGroup(context) {
    private val mHorizontalSpace = dp2px(16.0F)
    private val mVerticalSpace = dp2px(8.0F)

    private var allLinesViews = mutableListOf<List<View>>()//记录所有的行,用于layout
    private var lineHeights = mutableListOf<Int>()

    constructor(context: Context, attributes: AttributeSet) : this(context)

    constructor(context: Context, attributes: AttributeSet, defStyleAttr: Int) : this(context)

    private fun clearMeasureParams() {
        allLinesViews.clear()
        lineHeights.clear()
    }

    @SuppressLint("DrawAllocation")
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        clearMeasureParams()//防止频繁创建空间而发生OOM
        //本View的宽高
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)

        var lineViews = mutableListOf<View>()//记录一行中已经添加了的View
        var usedWidth = 0//记录已经用了的宽度
        var lineHeight = 0//记录已经用了的行高

        var childNeededParentWidth = 0//所有子view加起来需要父控件给的宽度
        var childNeededParentHeight = 0//所有子view加起来父控件需要给的高度

        //先度量所有子view的宽高--->view是树形结构,递归度量
        for (i: Int in 0 until childCount) {
            //度量子view的宽高
            children.elementAt(i).measure(
                getChildMeasureSpec(
                    widthMeasureSpec,
                    paddingLeft + paddingRight,
                    children.elementAt(i).layoutParams.width
                ),
                getChildMeasureSpec(
                    heightMeasureSpec,
                    paddingTop + paddingBottom,
                    children.elementAt(i).layoutParams.height
                )
            )
            //获取子view的宽高
            val childMeasuredWidth: Int = children.elementAt(i).measuredWidth
            val childMeasuredHeight: Int = children.elementAt(i).measuredHeight

            //换行
            if (usedWidth + childMeasuredWidth + mHorizontalSpace >= width) {
                allLinesViews.add(lineViews)
                lineHeights.add(lineHeight)

                //宽度取需要父控件给的宽度和已经使用的宽度的最大值--->通常情况下,如果没到边界,子view所需要父控件所给与的宽度就是子view已经使用的宽度
                childNeededParentWidth = max(childNeededParentWidth, usedWidth + mHorizontalSpace)
                //高度就是一直累加
                childNeededParentHeight += lineHeight + mVerticalSpace
                lineViews = mutableListOf()//是否能用clear?
                usedWidth = 0
                lineHeight = 0
            }
            //流式布局是分行布置的,所以要先把每个已经算好的子view添加到每一行中,然后再把每一行添加到所有行中--->两个list
            lineViews.add(children.elementAt(i))
            //每行的宽高
            usedWidth += childMeasuredWidth + mHorizontalSpace
            lineHeight = max(lineHeight, childMeasuredHeight)

            //处理最后一行
            if (i == childCount - 1) {
                allLinesViews.add(lineViews)
                lineHeights.add(lineHeight)
                childNeededParentWidth = max(childNeededParentWidth, usedWidth + mHorizontalSpace)
                childNeededParentHeight += lineHeight + mVerticalSpace
            }
        }
        //再度量自身的viewGroup宽高---->作为一个vp,他自己也是一个view,它的大小也需要根据父view的大小来一起度量才能决定
        val widthViewMode: Int = MeasureSpec.getMode(widthMeasureSpec)
        val heightViewMode: Int = MeasureSpec.getMode(heightMeasureSpec)
        val realWidth = if (widthViewMode == MeasureSpec.EXACTLY) width else childNeededParentWidth
        val realHeight =
            if (heightViewMode == MeasureSpec.EXACTLY) height else childNeededParentHeight
        //度量自己
        setMeasuredDimension(realWidth, realHeight)
    }

    override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
        //1.先获得此view的左上坐标
        var currentLeft = paddingLeft
        var currentTop = paddingTop
//        val allLineCount = allLinesViews.size
        //2.取出每一行都进行位置计算
        for (i in allLinesViews.indices) {
            val lineHeight = lineHeights[i]
            //3.取出每一行的每个element进行位置计算
            val lineView = allLinesViews[i]
            for (j in lineView.indices) {
                val view = lineView[j]
                val left = currentLeft
                val top = currentTop
                val right = left + view.measuredWidth
                val bottom = top + view.measuredHeight
                view.layout(left, top, right, bottom)
                //移动坐标,准备行内下一个元素的放置
                currentLeft = right + mHorizontalSpace
            }
            currentTop += lineHeight + mVerticalSpace
            //一行放置完毕,需要重置左边坐标
            currentLeft = paddingLeft
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值