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 对Layout_weight属性完全解析以及使用ListView来实现表格

今天主要说的是对Layout_weight属性的完全解析,以及利用Layout_weight这个属性使用ListView来实现表格的效果,我们都知道Android里面专门有一个TableLayout来...
  • xiaanming
  • xiaanming
  • 2013年10月31日 22:40
  • 33039

安卓开发技巧一:深入理解Android布局中Layout_weight的属性

今天开始将要为大家介绍一些安卓开发过程将要用到的一些技巧,这些技巧全部来自网络搜集,或者自己在企业做项目的时候总结出来的,利用这些技巧将会对我们开发带来非常方便的便捷性。 先来记录一下这一段时间的技巧...
  • u011131296
  • u011131296
  • 2014年12月08日 13:58
  • 1734

日积月累:android:layout_weight属性详解

在开发的过程中,为了布局更好的适配各种各样的屏幕,会经常使用android:layout_weight属性,按比例分配屏幕的空间。在很多资料和书籍中解释说,系统根据layout_weight比例分配占...
  • p106786860
  • p106786860
  • 2013年08月23日 09:02
  • 2983

android:layout_weight 属性的工作原理

android:layout_weight属性 字母上的意思是权重,这常常让人误解为整个空间的划分权重之比。但其实它指的仅仅是剩余空间划分的权重(比如剩余宽度、剩余高度)。而所谓的剩余空间当然就是指的...
  • m0_37222746
  • m0_37222746
  • 2017年01月13日 11:03
  • 309

Android中layout_weight属性设置规则

之前简单了解过weight属性原理,但是好长时间没用,今天突然被问起,竟一时想不出来怎么回事了,回来总结下,也算是一个备忘了。 首先了解下weight属性的意义:(这里只考虑宽)规定本控件可继续获得...
  • sky_cui
  • sky_cui
  • 2015年06月11日 14:16
  • 1239

Android layout_width和layout_weight关系

我们以实际的一个一个的小demo来说明问题demo01demo02demo03总结:FR:徐海涛(Hunk Xu)...
  • qq_15267341
  • qq_15267341
  • 2016年09月19日 21:42
  • 1119

在android布局中使用layout_weight的几点技巧和说明layout_weight

android应用中的layout_weight的使用技巧
  • yeah0126
  • yeah0126
  • 2015年09月25日 18:44
  • 2044

android:layout_weight总有你不知道的用法

之前对android:layout_weight的用法大概只知道下面第一种用法,后来在做“类似微信主界面”的时候,遇到了怎样将FrameLayout占据除底部标签栏之外的空间的问题,也就是下文中第二种...
  • andygo_520
  • andygo_520
  • 2016年07月07日 11:35
  • 503

Android:layout_weight属性的两种用法

weight属性用于设置控件长和宽所占的比例 首先看一个例子: Example.1
  • u013317646
  • u013317646
  • 2016年03月20日 19:06
  • 175

Android布局中的layout_weight和weightSum属性的详解及使用

由于Android设备的尺寸大小不一,种类繁多,当我们在开发应用的时候就要考虑屏幕的适配型了,尽可能让我们的应用适用于主流机型的尺寸,这样我们的应用不会因为尺寸不同而不美观,解决屏幕适配问题的方法有很...
  • android_cmos
  • android_cmos
  • 2016年03月02日 22:53
  • 7465
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android之layout_weight属性
举报原因:
原因补充:

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