android之layout_weight属性

原创 2017年09月18日 17:52:00

我们知道在xml布局的时候,LinearLayout下面的子控件可以使用layout_weight属性,那么我们根据源码,来分析下这个属性的一些知识点。


首先,在系统代码attrs.xml,我们可以知道对这个的定义,还有一个跟weight有关的weightSum的定义。

<declare-styleable name="LinearLayout_Layout">
        <attr name="layout_width" />
        <attr name="layout_height" />
        <attr name="layout_weight" format="float" /> 这个就是layout_weight的定义
        <attr name="layout_gravity" />
</declare-styleable>

<declare-styleable name="LinearLayout">
<attr name="weightSum" format="float" /> weightSum是LinearLayout的属性值

然后我们在LinearLayout.java中
找到LinearLayout配对的LayoutParams,里面
public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            TypedArray a =
                    c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
            weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
            gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
            a.recycle();
 }
所以从这里我们知道layout_weight是专属于LinearLayout的子控件所用,也只有在LinearLayout下才起作用。我们在xml中在LinearLayout下定义其子view,会自动为该子view生成LinearLayout.LayoutParams的布局参数的。在代码中,要显示定义。


layout_weight属性是跟控件大小相关的,属于measure,所以我们可以看下LinearLayout的onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);//我们选择水平布局来看
        }
    }

measureHorizontal代码很长,我们只看跟weight相关的代码

voidmeasureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
        mTotalLength = 0;//LinearLayout已经被使用了的大小
        .......
        float totalWeight = 0;//LinearLayout的所有子view总的weight


        final int count = getVirtualChildCount();
        
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
   
        final boolean isExactly = widthMode == MeasureSpec.EXACTLY;

        int usedExcessSpace = 0;

        // See how wide everyone is. Also remember max height.
        for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);
            if (child == null) {
                mTotalLength += measureNullChild(i);
                continue;
            }
           
            if (child.getVisibility() == GONE) {
                i += getChildrenSkipCount(child, i);
                continue;
            }


            if (hasDividerBeforeChildAt(i)) {
                mTotalLength += mDividerWidth;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            totalWeight += lp.weight;

            final boolean useExcessSpace = lp.width == 0 && lp.weight > 0;//我们useExcessSpace 为false,以为lp.width是-1和-2这两种情况
            if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) {
                ......
            } else {
                if (useExcessSpace) {
                   .......
                }

                // 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).
                final int usedWidth = totalWeight == 0 ? mTotalLength : 0;
                measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth,
                        heightMeasureSpec, 0);//第一次测量子view的大小,因为有weight,所以会有第二次测量


                final int childWidth = child.getMeasuredWidth();
                if (useExcessSpace) {
                    ......
                }


                if (isExactly) {//我们默认LinearLayout是占据屏幕宽度来分析
                    mTotalLength += childWidth + lp.leftMargin + lp.rightMargin
                            + getNextLocationOffset(child);

                } else {
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin
                            + lp.rightMargin + getNextLocationOffset(child));
                }

          .......

        }

        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
            mTotalLength += mDividerWidth;
        }


        if (useLargestChild &&//useLargestChild 默认为false
                (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
            ......
        }


        // Add in our padding
        mTotalLength += mPaddingLeft + mPaddingRight;
        
        int widthSize = mTotalLength;
        
        // Check against our minimum width
        widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
        
        // Reconcile our calculated size with the widthMeasureSpec
        int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);//根据父亲的MeasureSpec参数测量LinearLayout的大小,这里是占据整个屏幕
        widthSize = widthSizeAndState & MEASURED_SIZE_MASK;

        
        // Either expand children with weight to take up available space or
        // shrink them if they extend beyond our current bounds. If we skipped
        // measurement on any children, we need to measure them now.
        int remainingExcess = widthSize - mTotalLength//下面的为0,根据注释我们也知道,这个值就是剩余可用空间的大小
                + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);

        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
            float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;//这里mWeightSum 为0,所以remainingWeightSum 就是刚刚遍历所有子view后所有的weight的相加值
            mTotalLength = 0;

            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
                if (child == null || child.getVisibility() == View.GONE) {
                    continue;
                }

                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                final float childWeight = lp.weight;
                if (childWeight > 0) {
                    final int share = (int) (childWeight * remainingExcess / remainingWeightSum);//这里就是重新测量,剩余可用空间的百分比
                    remainingExcess -= share;
                    remainingWeightSum -= childWeight;


                    final int childWidth;
                    if (mUseLargestChild && widthMode != MeasureSpec.EXACTLY) {
                        childWidth = largestChildWidth;
                    } else if (lp.width == 0 && (!mAllowInconsistentMeasurement
                            || widthMode == MeasureSpec.EXACTLY)) {
                        // This child needs to be laid out from scratch using
                        // only its share of excess space.
                        childWidth = share;
                    } else {
                        // This child had some intrinsic width to which we
                        // need to add its share of excess space.
                        childWidth = child.getMeasuredWidth() + share;
                    }


                    final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                            Math.max(0, childWidth), MeasureSpec.EXACTLY);
                    final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
                            lp.height);
                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);//第二次设置子view的大小

                }

        }
        ......//这里是测量LinearLayout的大小,不是我们这次的主题
    }


所以根据上面的源码后,我们可以这样总结:

1.layout_weight只有LinearLayout的子view可以用

2.所有有layout_weight的子view需要经历两次measure,一次是正常measure,一次是根据linearlayout最后剩余的可用空间结合layout_weight计算其share,然后和原来的大小相加

3.所以,当子view都为match_parent,最后计算后,layout_weight大的反而最后占据的空间小。而wrap_content则相反。

4.例如有两个子view A和B,都是match_parent,layout_weight分别为1,2,所以最后A的空间为1+ 1*(1 - (1+1))/(1+2) = 2/3,而B的空间则是1/3。 

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android LinearLayout布局的layout_weight属性探究

Android布局文件中的layout_weight属性仅在LinearLayout布局中有效。 google推荐:当设置了控件的layout_weight属性时,该控件相应的layout_width...

android属性之layout_weight深入研究

在android布局开发中,LinearLayout用的挺多的,其中的layout_weight属性令新手很头疼,今天我总结了一下

android:layout_weight属性详解

在android开发中LinearLayout很常用,LinearLayout的内控件的android:layout_weight在某些场景显得非常重要,比如我们需要按比例显示。android并没用提...

Android 对Layout_weight属性完全解析以及使用ListView来实现表格

今天主要说的是对Layout_weight属性的完全解析,以及利用Layout_weight这个属性使用ListView来实现表格的效果,我们都知道Android里面专门有一个TableLayout来...

Android中的layout_weight属性详解

Android开发中,不可避免的都会使用到Layout_weight属性,也是面试题木钟常考的知识点。layout_weight属性在屏幕适配等方面有很大的作用。本文将从一个初学者的角度总结layou...

Android-Layout_weight属性解析(慕课网笔记)

来自http://www.imooc.com/video/10165 参考:http://www.cnblogs.com/JohnTsai/p/4074643.html 1 baselineAli...

android:layout_weight 属性详解

android:layout_weight 属性 详解 【一】计算方法 剩余总宽度 = 总宽度 - 声明总宽度 W* = W - W' W* =W -(w'1+w'2+w'3) 最终实际宽度 = ...

android:layout_weight属性详解

在android开发中LinearLayout很常用,LinearLayout的内控件的android:layout_weight在某些场景显得非常重要,比如我们需要按比例显示。android并没用提...

Android 对Layout_weight属性完全解析以及使用ListView来实现表格

From:http://blog.csdn.net/xiaanming/article/details/13630837

Android Layout_weight属性完全解析以及使用ListView来实现表格

转载请注明出处:http://blog.csdn.net/xiaanming/article/details/13630837 今天主要说的是对Layout_weight属性的完全解析,以及利用La...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)