ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

自定义viewGroup测量以及子view布局

2021-07-02 10:31:23  阅读:184  来源: 互联网

标签:viewGroup MeasureSpec 自定义 viewGroupHeightUsed child viewGroupWidthUsed widthMeas


自定义viewGroup测量以及子view布局

通常上,自定义viewGroup需要给子view进行测量,布局两个步骤,今天我们看看简单的自定义标签布局应该怎么实现

假如我以及子view全部测量好了,那我只要在onlayout里面

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        for ((index, child) in children.withIndex()) {
            child.layout(list[index].left, list[index].top, list[index].right, list[index].bottom)
        }
    }

美滋滋,把坐标全部填入就行了,那子view应该怎么测量呢

我们需要考虑两个因素,一个是开发者对Taglayout的要求,一个是开发者对子view的要求,以测量子view的宽度为例子

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val viewGroupWidthSize = MeasureSpec.getSize(widthMeasureSpec)
        val viewGroupWidthMode = MeasureSpec.getMode(widthMeasureSpec)
        var viewGroupWidthUsed = 0
        var viewGroupHeightUsed = 0

        for ((index, child) in children.withIndex()) {
            var layoutParams = child.layoutParams
            var childWidthMode = 0
            var childWidthSize = 0
            when (layoutParams.width) {
                MATCH_PARENT -> {
                    when (viewGroupWidthMode) {
                        MeasureSpec.EXACTLY -> {
                            childWidthMode = MeasureSpec.EXACTLY
                            childWidthSize =
                                MeasureSpec.getSize(widthMeasureSpec) - viewGroupWidthUsed
                        }
                        MeasureSpec.AT_MOST -> {
                            childWidthMode = MeasureSpec.AT_MOST
                            childWidthSize =
                                MeasureSpec.getSize(widthMeasureSpec) - viewGroupWidthUsed
                        }
                        MeasureSpec.UNSPECIFIED -> {
                            childWidthSize = MeasureSpec.UNSPECIFIED
                            childWidthSize = 0
                        }
                    }
                }
                WRAP_CONTENT -> {
                    when (viewGroupWidthMode) {
                        MeasureSpec.EXACTLY -> {
                            childWidthMode = MeasureSpec.AT_MOST
                            childWidthSize =
                                MeasureSpec.getSize(widthMeasureSpec) - viewGroupWidthUsed
                        }
                        MeasureSpec.AT_MOST -> {
                            childWidthMode = MeasureSpec.AT_MOST
                            childWidthSize =
                                MeasureSpec.getSize(widthMeasureSpec) - viewGroupWidthUsed
                        }
                        MeasureSpec.UNSPECIFIED -> {
                            childWidthSize = MeasureSpec.UNSPECIFIED
                            childWidthSize = 0
                        }
                    }
                }
                else -> {
                    childWidthMode = MeasureSpec.EXACTLY
                    childWidthSize = layoutParams.width
                }

            }
            child.measure(
                MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
                MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode)
            )
            if (index >= list.size) {
                list.add(Rect())
            }
            list[index].set(
                viewGroupWidthUsed,
                viewGroupHeightUsed,
                viewGroupWidthUsed + child.measuredWidth,
                viewGroupHeightUsed + child.measuredHeight
            )

        }

我们就是这样测量宽度的,父view是精确值,子view是填满,所以子view的宽度就确定了

父view是限制大小模式,子view是填满,那子view是精确值模式,值是父view被限制的大小,其实这一块代码应该抽出来,如果没有什么特俗要求,我们可以使用Google帮我们封装好的方法

measureChildWithMargins(child,widthMeasureSpec,viewGroupWidthUsed,heightMeasureSpec,viewGroupHeightUsed)
protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        **final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();**

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

因为measureChildWithMargins里面用的MarginLayoutParams所以我们需要在我们TagLayout里面重写getLayoutParams

override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }

重构后的onMeasure

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var viewGroupWidthUsed = 0
        var viewGroupHeightUsed = 0
        var maxLineHeight = 0
        for ((index, child) in children.withIndex()) {
            measureChildWithMargins(
                child,
                widthMeasureSpec,
                viewGroupWidthUsed,
                heightMeasureSpec,
                viewGroupHeightUsed
            )
            if (index >= list.size) {
                list.add(Rect())
            }
            list[index].set(
                viewGroupWidthUsed,
                viewGroupHeightUsed,
                viewGroupWidthUsed + child.measuredWidth,
                viewGroupHeightUsed + child.measuredHeight
            )
            viewGroupWidthUsed += child.measuredWidth
            maxLineHeight =  max(maxLineHeight, child.measuredHeight)
        }
        viewGroupHeightUsed = maxLineHeight
        setMeasuredDimension(viewGroupWidthUsed, viewGroupHeightUsed)
    }

效果图

现在我们要开始做换行了,思路很简单,把宽度放开,让子view随便测,如果子view宽度+已用的宽度,大于父view宽度,则刷新可用高度,可用宽度,再次给子view测一次高宽

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var viewGroupWidthUsed = 0
        var viewGroupHeightUsed = 0
        var maxLineHeight = 0
        var maxLineWidth = 0
        for ((index, child) in children.withIndex()) {
            measureChildWithMargins(
                child,
                widthMeasureSpec,
                0,
                heightMeasureSpec,
                viewGroupHeightUsed
            )
            if (viewGroupWidthUsed + child.measuredWidth > MeasureSpec.getSize(widthMeasureSpec)) {
                viewGroupWidthUsed = 0
                viewGroupHeightUsed = maxLineHeight

                measureChildWithMargins(
                    child,
                    widthMeasureSpec,
                    0,
                    heightMeasureSpec,
                    viewGroupHeightUsed
                )
            }

            if (index >= list.size) {
                list.add(Rect())
            }
            list[index].set(
                viewGroupWidthUsed,
                viewGroupHeightUsed,
                viewGroupWidthUsed + child.measuredWidth,
                viewGroupHeightUsed + child.measuredHeight
            )
            viewGroupWidthUsed += child.measuredWidth
            maxLineHeight =  max(maxLineHeight, child.measuredHeight + viewGroupHeightUsed)
            maxLineWidth = max(maxLineWidth,viewGroupWidthUsed)
        }
        viewGroupHeightUsed = maxLineHeight
        viewGroupWidthUsed = maxLineWidth
        setMeasuredDimension(viewGroupWidthUsed, viewGroupHeightUsed)
    }

效果图

标签:viewGroup,MeasureSpec,自定义,viewGroupHeightUsed,child,viewGroupWidthUsed,widthMeas
来源: https://www.cnblogs.com/c-rouxi/p/14962186.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有