Android自定义ViewGroup实现流式布局

最终效果

自定义ViewGroup

详细代码

class TagLayout : ViewGroup {
    /**
     * 所有view的集合
     */
    private var mViews: MutableList<MutableList<View>> = mutableListOf()

    /**
     * 每一行存储的view
     */
    private var mLineViews: MutableList<View> = mutableListOf()

    /**
     * 每一行view的最大高度
     */
    private var mHeights: MutableList<Int> = mutableListOf()

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context, attrs, defStyleAttr
    )


    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //避免多次执行onMeasure导致的问题
        mViews = mutableListOf()
        mLineViews = mutableListOf()
        mHeights = mutableListOf()
        //获取限制的值
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val paddingLeftRight = paddingLeft + paddingRight
        val paddingTopBottom = paddingTop + paddingBottom
        var lineWidth = 0 // 宽度为每一行view的宽度之和
        var lineHeight = 0 // 高度为每一行view的最高值
        var flowLayoutWidth = 0 //整体布局的宽度,取每一行view宽度的最大值
        var flowLayoutHeight = 0 // 取每一行view最高值之和
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChild(child, widthMeasureSpec, heightMeasureSpec)
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)

            val childLayoutParams: MarginLayoutParams = child.layoutParams as MarginLayoutParams
            val childWidth = child.measuredWidth
            val childHeight = child.measuredHeight
            val childMarginLeftRight = childLayoutParams.leftMargin + childLayoutParams.rightMargin
            val childMarginTopBottom = childLayoutParams.topMargin + childLayoutParams.bottomMargin

            if (lineWidth + paddingLeftRight + childWidth + childMarginLeftRight > widthSize) {
                mViews.add(mLineViews)
                mLineViews = mutableListOf()
                flowLayoutWidth = flowLayoutWidth.coerceAtLeast(lineWidth)
                flowLayoutHeight += lineHeight
                mHeights.add(lineHeight)
                lineWidth = 0
                lineHeight = 0
            }
            mLineViews.add(child)
            lineWidth += childWidth + childMarginLeftRight
            lineHeight = lineHeight.coerceAtLeast(childHeight + childMarginTopBottom)

            //处理最后一行显示问题,当遍历到最后一个view的时候,没有计算到整体中
            if (i == childCount - 1) {
                flowLayoutHeight += lineHeight
                flowLayoutWidth = flowLayoutWidth.coerceAtLeast(lineWidth)
                mHeights.add(lineHeight)
                mViews.add(mLineViews)
            }

        }
        //添加上padding
        flowLayoutWidth += paddingLeftRight
        flowLayoutHeight += paddingTopBottom

        setMeasuredDimension(
            if (widthMode == MeasureSpec.EXACTLY) widthSize else flowLayoutWidth,
            if (heightMode == MeasureSpec.EXACTLY) heightSize else flowLayoutHeight
        )
    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var currentX = paddingLeft
        var currentY = paddingTop
        for (i in 0 until mViews.size) {
            //每一行最高的高度进行累加
            val lineHeight = mHeights[i]
            val lineViews = mViews[i]
            for (lineView in lineViews) {
                val childLayoutParams: MarginLayoutParams =
                    lineView.layoutParams as MarginLayoutParams
                val left = currentX + childLayoutParams.leftMargin
                val top = currentY + childLayoutParams.topMargin
                val right = left + lineView.measuredWidth
                val bottom = top + lineView.measuredHeight
                //子view进行布局
                lineView.layout(
                    left,
                    top,
                    right,
                    bottom
                )
                currentX += right + childLayoutParams.rightMargin
            }
            currentX = paddingLeft
            currentY += lineHeight
        }

    }

    //需要重写此方法,否则子view无法获取margin (child.layoutParams as MarginLayoutParams 会抛异常)
    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }
}

小结

整体代码还是比较简单,但作为学习自定义ViewGroup还是十分不错的,不积跬步无以至千里!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值