当前位置: 移动技术网 > 移动技术>移动开发>Android > FlowLayout

FlowLayout

2020年07月17日  | 移动技术网移动技术  | 我要评论
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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网