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)    }}

更多相关文章

  1. Android官方文档翻译--How Android Draws Views
  2. android 坐标之间距离
  3. android recyclerView 设置item间的间距
  4. 自定义两行可左右滑动的GridView
  5. Android shape实现渐变、部分圆角及叠加效果
  6. Android APP 自定义水印(kotlin)
  7. ListView 可以滑动的上下间距
  8. android字间距实现,textview字间距实现
  9. LinearLayout增加divider分割线

随机推荐

  1. Android自定义控件的实现
  2. Android按两次返回键退出应用
  3. Android(安卓)Java压缩Zlib,Gzip,Zip支持
  4. S5PV210 三个Camera Interface/CAMIF/FIM
  5. Android基于UDP的局域网聊天通信(有完整De
  6. 【android】自定义ViewGroup的onLayout()
  7. Android的几种按钮控件: Options Menu / C
  8. Handler: 更新UI的方法
  9. 伊豆:把豆瓣网装进口袋
  10. Hello Android(安卓)- File文件操作