Android之Weight属性源码解析

转载请注明出处:http://blog.csdn.net/mybeta/article/details/39547141


LinearLayout是大家最喜欢的Android原生控件之一,相信大家对LinearLayout中的weight这一属性也不陌生,如果这个属性使用的好,能帮我们做出更加漂亮的界面。

比如下面的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Button1" />

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Button2" />

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Button3" />

</LinearLayout>

我们会得到这样一个界面:



三个按钮平均分配空间。这是我们最常用的一种使用方式,也是最简单的使用方式。所以大家一般都认为,weight的作用就是按比例来分配空间。这种说法在某些情况(比如上面的情况)下是正确的,但是并不全面,网上很多其他博客也指出了这一观点的问题所在,而且很多博客也给出了具体的weight值计算公式。但是我们依然需要了解weight到底是如何工作的。


在上面的布局文件中,我们的目的其实就是想让三个Button平均分配空间,也许大家注意到了上面三个Button的layout_width属性都是设置的0dp,为什么要这么设置呢?因为如果设置成别的值的话,也许结果就不是我们所期待的了,至于原因我们会在后面分析到。


weight属性显然影响了LinearLayout中子控件的大小,即影响了measure过程,所以我们定位到LinearLayout中的onMeasure()方法:【以下源码均基于Android2.2】

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

由于LinearLayout区分了水平方向和垂直方向,我们这里就拿垂直方向来分析。我们可以分为两个步骤来分析该过程:



步骤1:


measureVertical()方法比较长,我们分段来分析。该过程可以分为两个部分,我们先来看第一个部分:【注意:该方法中有部分内容是处理LinearLayout宽度的,由于我们这里分析的是高度上的处理过程,所以就忽略了对宽度处理的分析】

        mTotalLength = 0;
        int maxWidth = 0;
        int alternativeMaxWidth = 0;
        int weightedMaxWidth = 0;
        boolean allFillParent = true;
        float totalWeight = 0;

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

        boolean matchWidth = false;

        final int baselineChildIndex = mBaselineAlignedChildIndex;        
        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;
            }

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

            totalWeight += lp.weight; // 记录下总的weight值
            
            // 这三个条件同时满足才会不处理child
            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.
            	// 对于lp.weight>0的child,暂时不处理
            	
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
            } else {
                int oldHeight = Integer.MIN_VALUE;

                // 把lp.height设为WRAP_CONTENT
                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);

                // 恢复lp.height
                if (oldHeight != Integer.MIN_VALUE) {
                   lp.height = oldHeight;
                }

                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);
                }
            }

            /**
             * 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;
            }

            // 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;
            maxWidth = Math.max(maxWidth, measuredWidth);

            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);
        }


第1~18行是拿到或设置一些初始值,第1行中的mTotalLength指的是LinearLayout中所有child view的高度和,注意不是LinearLayout的高度。11行拿到高度的measure 模式。


进入21行的for循环,这里对LinearLayout的每一个child view依次进行处理。29行中跳过了visibility为GONE的child view。34行拿到了child view的LayoutParams,因为是在LinearLayout中,所以该LayoutParams的具体类型为LinearLayout.LayoutParams。36行中记录下总的weight值,注意如果在XML文件中没有设置layout_weight属性,那该child view的weight值为0。


接下来是关键部分。

我们先看39行的if条件,heightMode == MeasureSpec.EXACTLY代表LinearLayout的layout_height为MATCH_PARENT或者具体值(注意这并不是一个很好的解释,如果要了解这个值,需要去了解mode值的设置)。lp.height == 0 && lp.weight > 0就是指child view的layout_height为0,并且layout_weight>0。如果同时满足这三个条件,我们可以看到if语句里面对该child view没有进行任何的处理。

如果不能同时满足上面三个条件,进入else部分。进入了else,我们就对child view进行了measure处理。48~58行的作用是,如果一个child view设置了layout_weight,也设置了它的layout_height为0,那么为了让它进行一个有意义的measure,先将它的layout_weight设置为,WRAP_CONTENT。然后在69行恢复到原值。那么中间的部分就是对child view的measure过程了。我们看到64行,该方法的具体实现如下:

    void measureChildBeforeLayout(View child, int childIndex,
            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
            int totalHeight) {
        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
                heightMeasureSpec, totalHeight);
    }

其实它就是调用了child view的measure方法,而调用过后,我们就可以通过73行的getMeasuredHeight()方法拿到child view measure后的高度,74、75行我们会把这个高度记录到mTotalLength中去。要特别注意64行方法中的最后一个参数,它代表的意义是LinearLayout已经有多大的空间被占用了,在对child view measure的过程中,会先计算出LinearLayout可用的剩余空间,然后在这个剩余空间的基础上去决定child view的大小。在这里,如果totalWeight不为0,我们就忽略掉可用的剩余空间,而让该child view在总的可用空间的基础上去决定自身大小。剩下的代码可以忽略。



步骤2:


接下来分析第二部分的代码:

        // Add in our padding
        mTotalLength += mPaddingTop + mPaddingBottom;

        int heightSize = mTotalLength;

        // Check against our minimum height
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
        
        /*
         * 关键点:计算出当前LinearLayout的最终高度值。注意时当前的LinearLayout。
         * 
         */
        // Reconcile our calculated size with the heightMeasureSpec
        heightSize = resolveSize(heightSize, heightMeasureSpec);
        
        // Either expand children with weight to take up available space or
        // shrink them if they extend beyond our current bounds

        /**
         * delta<0,意味着空间不够,需要那些weight>0的视图腾出空间,weight越大,需要腾出的空间越大。
         * delta>0,意味着空间富余,所以把富余空间分配给那些weight>0的视图。
         */
        int delta = heightSize - mTotalLength;
        // 如果delta=0,就是说刚刚好占满空间,之前又有满足条件的没有进行measure的child,
        // 那这些child就不会再获得空间了。
        // 满足的条件是:heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0
        if (delta != 0 && totalWeight > 0.0f) {
            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;

            // 重新记录高度
            mTotalLength = 0;

            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
                
                if (child.getVisibility() == View.GONE) {
                    continue;
                }
                
                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
                
                float childExtra = lp.weight;
                if (childExtra > 0) { // 只处理weight>0的
                    // Child said it could absorb extra space -- give him his share
                    int share = (int) (childExtra * delta / weightSum);
                    weightSum -= childExtra;
                    delta -= share;

                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            mPaddingLeft + mPaddingRight +
                                    lp.leftMargin + lp.rightMargin, lp.width);

                    // TODO: Use a field like lp.isMeasured to figure out if this
                    // child has been previously measured
                    // 这里就是看child之前有没有measure过,满足(heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0)
                    // 这个条件的就是没有被measure过的
                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
                        // child was measured once already above...
                        // base new measurement on stored values
                        int childHeight = child.getMeasuredHeight() + share;
                        if (childHeight < 0) {
                            childHeight = 0;
                        }
                        
                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
                    } else {
                        // child was skipped in the loop above.
                        // Measure for this first time here      
                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
                                        MeasureSpec.EXACTLY));
                    }
                }

                final int margin =  lp.leftMargin + lp.rightMargin;
                final int measuredWidth = child.getMeasuredWidth() + margin;
                maxWidth = Math.max(maxWidth, measuredWidth);

                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
                        lp.width == LayoutParams.MATCH_PARENT;

                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);

                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

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

            // 新的总高度(LinearLayout内容的高度)
            // 这个值在layout过程中还会被用到
            // Add in our padding
            mTotalLength += mPaddingTop + mPaddingBottom;
            // TODO: Should we recompute the heightSpec based on the new total length?
        } else {
            alternativeMaxWidth = Math.max(alternativeMaxWidth,
                                           weightedMaxWidth);
        }

        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
            maxWidth = alternativeMaxWidth;
        }
        
        maxWidth += mPaddingLeft + mPaddingRight;

        // Check against our minimum width
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
        
        setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);

        if (matchWidth) {
            forceUniformWidth(count, heightMeasureSpec);
        }


这一部分主要是对weight进行处理,我们平时所看到的weight值形成的效果都是在一部分中实现的。第2行就是加上LinearLayout本身的在垂直方向上的padding,mTotalLength这时候是LinearLayout所有内容的高度。接下来需要得到LinearLayout本身的高度,14行得到的heightSize就是LinearLayout本身的高度,我们看一下resolveSize()方法:

    public static int resolveSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize =  MeasureSpec.getSize(measureSpec);
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
            result = Math.min(size, specSize);
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

该方法很简单,如果LinearLayout的layout_height为MATCH_PARENT,那么最终的高度就是LinearLayout父控件的高度;如果LinearLayout的layout_height为WARP_CONTENT,那么最终的高度就是LinearLayout父控件的高度和LinearLayout中内容的高度中的最小值。(这个说法不准确)

继续看上面的代码,23行得到一个delta值,该值是LinearLayout本身的高度和LinearLayout内容的高度的一个差值,如果delta>0,代表LinearLayout还有多余的空间,delta<0,代表现有的内容已经超出了LinearLayout的容纳范围。27的if语句主要就是对weight值进行处理,根据weight值进行空间的重新分配,如果delta=0,我们认为measure完成了,不对weight值进行其他的处理了。如果totalWeight值小于或者等于0,我们认为没有设置weight值或者设置的不准确,也不需要对weight进行处理了。


28行可以保证weightSum为正数,如果没有在LinearLayout中设置weightSum,那它的默认值为-1。如果进来if语句,就需要对所有的或者部分child view重新分配空间,即需要重新measure,所以31行将mTotalLength重置为0。33行这里又有一个for循环,做的是和第一部分中的for循环类似的工作。76~86行是处理宽度的,暂且不管。我们看到89行,这里又将child.getMeasuredHeight()加到mTotalLength中去了,所以这个for循环也是将LinearLayout中每一个child view measure后,将它们的measuredHeight累加起来。


那么在这之前又对child view做了怎么的处理呢?我们来到最为核心的43 ~74行。从43行的条件可以看出,我们只对那些weight>0的child view进行重新的measure处理,如果该child view设置的weight值小于0,或者没有设置,这里都不需要处理这个child view,会直接来到89行。45行都得到一个share值,该值是通过weight值按比例获取到的,即按比例平分上面的delta值。46、47行保证下一次循环的时候,同样根据weight值按比例分配剩下的delta值。比如A:1,B:2,C:1,D:1,(冒号后面为weight值),delta=10,那么A:1 / 5 * 10 = 2, B:2 / (5 - 1) * (10 - 2) = 4, C:1 / (5 - 1 - 2) * (10 - 2 - 4) = 2, D:1 / (5 - 1 - 2 - 1) * (10 - 2 - 4 - 2) = 2 。其实和按比例分配是一样的。


如果满足57行的条件,即一个child view的weight值大于0,并且在之前经过了measure过程,60行就是在child view已有的高度上加上一个share值,65行重新measure一次,注意这里重新measure的时候使用的是MeasureSpec.EXACTLY,即这个值是确定了的。姑且一看这里没什么可以深究的,但是当share<0的时候呢?显然会减小child view的高度,并且显然可知,如果weight值越大,在这里减小的程度就越大,最终的高度也就越小,这里就和我们平常所认为的weight值越大,控件也越大的观点相反了。我们再看到70行,如果child view在之前没有measure过(即满足第一部分代码中39行的条件),在这里会直接用share值作为child view的高度,注意这里也是MeasureSpec.EXACTLY,如果是这种情况的话,weight值越大,我们的控件也就越高,和我们平常的观点一致。这也是为什么在文章开头的例子中设置所有的Button的layout_wight为0dp的原因。



总结:


对LinearLayout的measure过程做一个总结:

条件:LinearLayout的heightMode=MeasureSpec.EXACTLY,child view的lp.weight>0并且lp.height=0

1. 遍历所有的child view进行measure,暂时跳过满足上面条件的child view。注意如果totalWeight不为0的话,在对child view进行measure的时候是以LinearLayout的高度为基础的。

2. 计算出一个差值delta,将这个值按比例分配到那些weight>0的child view上。如果delta<0,会减小那些weight>0的child view的大小。

3. 代码中并没有不允许weight<0的child view参与运算,但是如果weight<0,可能会出现某些意想不到的情况,建议不要用weight<0的值。




下面通过例子来具体了解weight的作用。

有这样一个布局:【默认布局】

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="fill_parent"
        android:layout_height="200dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/blue_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#0707F1"
            android:gravity="center_vertical"
            android:text="蓝色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/green_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#16E95A"
            android:gravity="center_vertical"
            android:text="绿色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/yellow_text"
            android:layout_width="fill_parent"
           android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#FFFF80"
            android:gravity="center_vertical"
            android:text="黄色"
            android:textColor="#000000" />

        <TextView
            android:id="@+id/black_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#000000"
            android:gravity="center_vertical"
            android:text="黑色"
            android:textColor="#ffffff" />
    </LinearLayout>

显示效果:



4个TextView平均分配空间。


修改布局变成如下:【布局1】

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="fill_parent"
        android:layout_height="200dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/blue_text"
            android:layout_width="fill_parent"
           android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#0707F1"
            android:gravity="center_vertical"
            android:text="蓝色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/green_text"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#16E95A"
            android:gravity="center_vertical"
            android:text="绿色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/yellow_text"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#FFFF80"
            android:gravity="center_vertical"
            android:text="黄色"
            android:textColor="#000000" />

        <TextView
            android:id="@+id/black_text"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#000000"
            android:gravity="center_vertical"
            android:text="黑色"
            android:textColor="#ffffff" />
    </LinearLayout>

显示效果:



结果只显示黄颜色部分,我们从代码的角度分析该结果。首先前两个TextView lp.height不为0,weight>0,所以会进行measure操作,因为它的lp.height>0,所以可知得到的measuredHeight为100(50dp转化而来),同理,第4个TextView也是进行同样的流程,它的measuredHeight也为100。然后看第3个TextView,它在步骤1中即可measure,并且最终measuredHeight为400(200dp转化而来),所以最终delta = 400 - 100 * 3 - 400 = -300。在步骤2中,第1、2、4个TextView的share值分别为:-100,-100,-100,所以最后这三个TextView的高度都为0。而第3个TextView因为没有weight值,它的高度最终就为400。


如果把布局1中的第3个TextView的layout_height改为wrap_content:【布局2】

显示结果:


这里分析过程同上,大家可以自行分析。


再来看一种布局,在默认布局的基础上修改:【布局3】

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="fill_parent"
        android:layout_height="200dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/blue_text"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#0707F1"
            android:gravity="center_vertical"
            android:text="蓝色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/green_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#16E95A"
            android:gravity="center_vertical"
            android:text="绿色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/yellow_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#FFFF80"
            android:gravity="center_vertical"
            android:text="黄色"
            android:textColor="#000000" />

        <TextView
            android:id="@+id/black_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#000000"
            android:gravity="center_vertical"
            android:text="黑色"
            android:textColor="#ffffff" />
    </LinearLayout>

显示效果:



第1个TextView weight>0,但是lp.height不为0,所以会进行measure,measuredHeight为100(50dp),而后面的3个TextView在步骤1中都不会measure,所以delta = 400 - 100 = 300,在步骤2中,每一个TextView的share都为 300 / 4 = 75,所以第1个TextView最终高度就为100 + 75 = 175,后面3个TextView都为75。


我们再来看一个weight值为负数的,【布局4】

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="fill_parent"
        android:layout_height="200dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/blue_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#0707F1"
            android:gravity="center_vertical"
            android:text="蓝色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/green_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#16E95A"
            android:gravity="center_vertical"
            android:text="绿色"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/yellow_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#FFFF80"
            android:gravity="center_vertical"
            android:text="黄色"
            android:textColor="#000000" />

        <TextView
            android:id="@+id/black_text"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="-1"
            android:background="#000000"
            android:gravity="center_vertical"
            android:text="黑色"
            android:textColor="#ffffff" />
    </LinearLayout>


显示效果:



前3个TextView在步骤1的都会跳过measure过程,第4个TextView在步骤1的时候虽然不会跳过,但是它的lp.height=0,并且没有变成WRAP_CONTENT,参考步骤1中的第51行代码,lp.weight>0时才会设置为WARP_CONTENT。所以它的measuredHeight=0,delta=400,weightSum=1 + 1 + 1 + (-1) = 2,所以在步骤2中,第1个share=400 * 1 / 2 = 200,即第一个TextView的高度为200,第2个share = 200 * 1 / 1 = 200,即第二个TextView高度为200,到第3个TextView的时候,share=0,所以第3个TextView高度为0,至于第4个TextView,在步骤1中得到的高度就为0,而在步骤2中没有对它进行处理,所以它的高度依然为0。


所以,只要我们了解清楚了weight的具体工作流程,就不用记一些计算公式了。当然,我们用的最多的还是【默认布局】。


点击下载源码!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值