FlowLayout
16lz
2021-01-24
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// 流式布局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) }}
更多相关文章
- Android官方文档翻译--How Android Draws Views
- android 坐标之间距离
- android recyclerView 设置item间的间距
- 自定义两行可左右滑动的GridView
- Android shape实现渐变、部分圆角及叠加效果
- Android APP 自定义水印(kotlin)
- ListView 可以滑动的上下间距
- android字间距实现,textview字间距实现
- LinearLayout增加divider分割线