LinearLayout 和 RelativeLayout onMeasure理解(一)

记录自己的理解,如果有错误 请大家指正 谢谢

人们说估计使用RelativeLayout而不是LinearLayout,因为LinearLayout比relativeLayout多测量一次,事实上是这样吗?我们来详细看看

1、LinearLayout 有两种模式,VERTICAL和HORIZONTAL所以也有两种测量方式

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

2、现在我们就对使用多一点的VERTICAL学习一下onMeasure过程

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
        mTotalLength = 0;//linearlayout 自己所占的高度比如DividerHeight
        int maxWidth = 0;//
        int childState = 0;//child状态 是否有觉得父类给的大小太小
        int alternativeMaxWidth = 0;//暂时未知
        int weightedMaxWidth = 0;//权重状态下的最大宽度
        boolean allFillParent = true;
        float totalWeight = 0;//总的权重值

        final int count = getVirtualChildCount();//获取child数量 有个虚拟的修饰,跟踪进去也没理解
        
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        boolean matchWidth = false;
        boolean skippedMeasure = false;

        final int baselineChildIndex = mBaselineAlignedChildIndex;  //基线,你学英文的时候三条横线就是基线,大家可以试下设置这个属性然后看下几个child有什么不同      
        final boolean useLargestChild = mUseLargestChild;//从名字感觉没懂

        int largestChildHeight = Integer.MIN_VALUE;

        // See how tall everyone is. Also remember max width.
        for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);

            if (child == null) {
                mTotalLength += measureNullChild(i);//方法返回0,不知道为什么这样设计
                continue;
            }

            if (child.getVisibility() == View.GONE) {
               i += getChildrenSkipCount(child, i);//方法返回0
               continue;
            }

            if (hasDividerBeforeChildAt(i)) {//确定child前面是否有分割线
                mTotalLength += mDividerHeight;
            }

            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

            totalWeight += lp.weight;
            //这里就可以看出为什么要鼓励大家设置weight的时候最好将宽或者高设置为0
            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
                // Optimization: don't bother measuring children who are going to use
                // leftover space. These views will get measured again down below if
                // there is any leftover space.
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                skippedMeasure = true;
            } else {
                int oldHeight = Integer.MIN_VALUE;

                if (lp.height == 0 && lp.weight > 0) {
                    // heightMode is either UNSPECIFIED or AT_MOST, and this
                    // child wanted to stretch to fill available space.
                    // Translate that to WRAP_CONTENT so that it does not end up
                    // with a height of 0
                    oldHeight = 0;
                    lp.height = LayoutParams.WRAP_CONTENT;
                }

                // Determine how big this child would like to be. If this or
                // previous children have given a weight, then we allow it to
                // use all available space (and we will shrink things later
                // if needed).
                measureChildBeforeLayout(
                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
                       totalWeight == 0 ? mTotalLength : 0);// LinearLayout第一次测量

                if (oldHeight != Integer.MIN_VALUE) {//如果linearLayout本身没设置match,并且child的初始lp.height == 0 && lp.weight > 0
                   lp.height = oldHeight;//那么就把高度设置为0
                }

                final int childHeight = child.getMeasuredHeight();
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                       lp.bottomMargin + getNextLocationOffset(child));

                if (useLargestChild) {
                    largestChildHeight = Math.max(childHeight, largestChildHeight);//对比找出最高的child大小
                }
            }
从代码可以看出  linearLayout设置match 然后child设置height=0,weight>0 就可以少测量一次,那么你就优化了一把你的APP了,其中测量child暂时不看,关于width的暂时不看,与我们研究的无关。我们继续往下看

 /**
             * If applicable, compute the additional offset to the child's baseline
             * we'll need later when asked {@link #getBaseline}.
             */
            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
               mBaselineChildTop = mTotalLength;//如果你设置了baselineChildIndex属性,并且这里匹配到了
            }

            // if we are trying to use a child index for our baseline, the above
            // book keeping only works if there are no children above it with
            // weight.  fail fast to aid the developer.
            if (i < baselineChildIndex && lp.weight > 0) {
                throw new RuntimeException("A child of LinearLayout with index "
                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
                        + "won't work.  Either remove the weight, or don't set "
                        + "mBaselineAlignedChildIndex.");
            }

            boolean matchWidthLocally = false;
            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
                // The width of the linear layout will scale, and at least one
                // child said it wanted to match our width. Set a flag
                // indicating that we need to remeasure at least that view when
                // we know our width.
                matchWidth = true;
                matchWidthLocally = true;
            }

            final int margin = lp.leftMargin + lp.rightMargin;
            final int measuredWidth = child.getMeasuredWidth() + margin;//如果是上面没有测量child,那么这里getMeasureWidth应该返回0
            maxWidth = Math.max(maxWidth, measuredWidth);
            childState = combineMeasuredStates(childState, child.getMeasuredState());//如果是上面没有测量child 状态设置也无意义了

            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
            if (lp.weight > 0) {//有权重和没有 连宽度都能受到影响
                /*
                 * Widths of weighted Views are bogus if we end up
                 * remeasuring, so keep them separate.
                 */
                weightedMaxWidth = Math.max(weightedMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            } else {
                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            }

            i += getChildrenSkipCount(child, i);//不知道有什么意义 i += 0 ?
        }

至此第一个for循环取child完了,主要完成mTotalLength和alternativeMaxWidth的读取;

还有第二次循环 ,主要是对权重的处理,如果child没有weight属性则不会进入,大家可以简单看下,忽略关于width的代码,因为我们这里主要分析高度方面的测量

3、总结一下

总的来说 linearout有两个循环测量child大小,第一个循环测量大小值然后确定linearout的高度;第二个循环是调整child的大小。由于LinearLayout含有weight,那么总是先测量不含weight的child的大小,剩下的大小由weight比例均分。

总结一下下面几种情况:

a、LinearLayout是match,child全是height=0,weight>0: 在这种情况下会跳过第一次for循环measure child,  经历第一次循环,mTotalLength只是含有divider值和child设置的margin值;接下来加上LinearLayout设置的paddingTop和paddingBottom;然后取出parent给的高度减去这些margin和padding消耗的值得到剩余的高度;将mTotalLength设置为0,根据child的weight与weightTotal的比值来给child设置高度,child.measure(childWidthMeasureSpec, MeasureSpec.makeMeasureSpec(XXXX,MeasureSpec.EXACTLY));将每次child测量的值加入到mTotalLength中,然后设置linearLayout的测量大小setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);

这种状态下 Linearout的高度就是parent传过来的heightMeasureSpec里面的值

b、LinearLayout是match,除了a中child的情况:将height不等于0的child都会测量一下大小,并且将测量的大小和Linearlayout总的大小 相减后 ,如果结果>0 则将这部分高度按weight均分再加上 上个循环测量的大小,重新给child测量;

这种情况大家可以实现一下,最后发现LinearLayout的child如果height设置为wrap,并且有权重值,那么你会发现他的大小和权重值分配到的大小不一致,会大许多。

c、LinearLayout是wrap_content:   如果child 是height=0,weight>0,那么在第一次循环测量的时候会把你的height设置为wrap_content,然后就没有然后了 你的weight失效了,

失效了



总的大家说linearLayout 会测量两次是有问题的,LinearLayout为wrap则,child只测量一次,如果LinearLayout为match,则child只有weight>0才有可能会测量两次,如果height=0 会测量一次,height不为0会测量两次。如果weight没有值则只测量一次



文章比较粗糙,请大家见谅 如果有说的不对的欢迎大家斧正 谢谢




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值