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