欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

FlowLayout

程序员文章站 2022-08-03 17:46:06
package com.xiangxue.nestedscroll.flowlayoutimport android.content.Contextimport android.content.res.Resourcesimport android.util.AttributeSetimport android.util.TypedValueimport android.view.Viewimport android.view.ViewGroupimport kotlin.math.max...
package com.xiangxue.nestedscroll.flowlayout

import android.content.Context
import android.content.res.Resources
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import kotlin.math.max

// 流式布局
class FlowLayout(context: Context?, attrs: AttributeSet?) : ViewGroup(context, attrs) {

    val TAG = "FLowLayout"
    var mHorizontalSpacing = dp2px(16F) //每个item横向间距
    var mVerticalSpacing = dp2px(8F) //每个item横向间距

    // 记录所有的行, 一行一行的存储,用于layout
    val allLines = arrayListOf<List<View>>()

    // 记录每一行的行高,用于layout
    val lineHeights = arrayListOf<Float>()


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

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //测量所有子View的宽高
        clearMeasureParams() //内存抖动
        //先度量孩子
        // ViewGroup 解析的父亲给我的宽度
        val selfWidth = MeasureSpec.getSize(widthMeasureSpec)
        // ViewGroup 解析的父亲给我的高度
        val selfHeight = MeasureSpec.getSize(heightMeasureSpec)

        //保存一行中的所有的View
        var lineViews = arrayListOf<View>() // 换行要重新赋值
        // 记录这行已经使用了多宽的size
        var lineWidthUsed = 0f
        // 一行的行高
        var lineHeight = 0f

        var parentNeededWidth = 0f //measure过程中,子View要求的ViewGroup的宽
        var parentNeededHeight = 0f //measure过程中,子View要求的ViewGroup的高

        for (i in 0 until childCount) {
            val childView = getChildAt(i)
            val childLP = childView.layoutParams
            if (childView.visibility != View.GONE) {
                // 将layoutparams转变为measureSpec
                val childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, childLP.width)
                val childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingLeft + paddingRight, childLP.height)
                childView.measure(childWidthMeasureSpec, childHeightMeasureSpec)

                //获取子View的度量宽高
                val childMeasuredWidth = childView.measuredWidth
                val childMeasureHeight = childView.measuredHeight

                //如果需要换行
                if (childMeasuredWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
                    // 一旦换行,我们就可以判断当前行需要的宽和高了,所以此时要记录下来
                    allLines.add(lineViews)
                    lineHeights.add(lineHeight)

                    parentNeededHeight += lineHeight + mVerticalSpacing
                    parentNeededWidth = max(parentNeededHeight, lineWidthUsed + mHorizontalSpacing)

                    lineViews = arrayListOf()
                    lineWidthUsed = 0f // 一行已经用过的宽度
                    lineHeight = 0f //一行的高度
                }

                // view是分行layout的,所以要记录每一行有哪些view, 这样可以方便layout布局
                lineViews.add(childView)
                //每行都会有自己的宽高
                lineWidthUsed += childMeasuredWidth + mHorizontalSpacing
                lineHeight = Math.max(lineHeight, childMeasureHeight.toFloat())

                //处理最后一行数据
                if (i == childCount - 1) {
                    allLines.add(lineViews)
                    lineHeights.add(lineHeight)
                    parentNeededHeight += lineHeight + mVerticalSpacing
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing)
                }
            }
        }


        // 再度量自己,保存
        // 根据子View的度量结果,来重新度量自己的ViewGroup
        // 作为一个ViewGroup, 它自己也是一个View, 它的大小也需要根据它的父亲给它提供的宽高来度量
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)

        //val realWidth = if (widthMode == MeasureSpec.EXACTLY) selfWidth else parentNeededWidth.toInt()
        //val realHeight = if (heightMode == MeasureSpec.EXACTLY) selfHeight else parentNeededHeight.toInt()
        setMeasuredDimension(selfWidth, selfHeight)
    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {

        val lineCount = allLines.size
        var curL = paddingLeft
        var curT = paddingTop

        for (i in 0 until lineCount) {
            val lineViews = allLines.get(i)

            val lineHeight = lineHeights.get(i)
            for (view in lineViews) {

                val right = curL + view.measuredWidth
                val bottom = curT + view.measuredHeight

                view.layout(curL, curT, right, bottom)
                curL = right + mHorizontalSpacing.toInt()
            }
            curT += lineHeight.toInt() + mVerticalSpacing.toInt()
            curL = paddingLeft
        }

    }


    private fun dp2px(dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().displayMetrics)
    }
}

本文地址:https://blog.csdn.net/AdrianAndroid/article/details/107386501

相关标签: Android 高级