LinearLayout的weight

来源:互联网 发布:樱井知香为什么喷泉 编辑:程序博客网 时间:2024/05/16 12:58

一、weight是怎么计算的

使用LinearLayout的时候,一定会遇到weight怎么使用的问题。

简单来讲,假设LinearLayout为竖直方向

子View 的高度 = 子View 的实际高度 + 分配到的高度

实际高度:是该View的height,例如:在xml中设置的android:layout_height=”8dp”(注意这里设置的不是wrap_content或者match_parent)

分配到的高度 : (LinearLayout自身的高度 - 所有子View的总高度) / weight总和 * 该子View的weight

LinearLayout自身的高度 - 所有子View的总高度 的含义为:LinearLayout的可分配高度

所以简单来说,子View的高度 = 自身高度 + LinearLayout分配给的高度。LinearLayout是用自身可分配的高度,分成“weight总和”份,然后再按照每个子View的weight值分配。

二、例子

1、先看没加weight的时候的样子:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="aaaaaaaaaaaaaaa"        android:background="#ff0000" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="bbb"        android:background="#00ff00" /></LinearLayout>

这里写图片描述

可以看到,第一个 TextView 的宽度为 15个a 的宽度,第二个 TextView 的宽度为 3个b 的宽度。

2、添加weight:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="aaaaaaaaaaaaaaa"        android:layout_weight="1"        android:background="#ff0000" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="bbb"        android:layout_weight="1"        android:background="#00ff00" /></LinearLayout>

这里写图片描述

这就是大家最常遇到的问题,就是明明 weight 设置的一样,但是实际上的效果并不是均分。

为什么会这样呢?
看第一张图(也就是没加weight的那张图),图中,第一行,除去红色和绿色的部分是:LinearLayout除去 所有 子View 的总宽度 之后剩下的宽度,这部分才是使用weight分配的部分。
所以,
第一个TextView的总宽度是:第一个TextView的实际宽度为15个a的宽度 + LinearLayout剩余部分的一半
第二个TextView的总宽度是:第二个TextView的实际宽度为3个b的宽度 + LinearLayout剩余部分的一半

于是,虽然weight相同,但是不均分。

3、实现均分:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:layout_width="0dp"        android:layout_height="wrap_content"        android:text="aaaaaaaaaaaaaaa"        android:layout_weight="1"        android:background="#ff0000" />    <TextView        android:layout_width="0dp"        android:layout_height="wrap_content"        android:text="bbb"        android:layout_weight="1"        android:background="#00ff00" /></LinearLayout>

这里写图片描述

其实想要均分的方法很简单,就是把每个 子View 的 layout_width 置为 0dp 即可。

为什么这样就可以了呢?
因为 所有子View 的 width 都为 0dp,所以,LinearLayout可分配的总宽度为全部宽度,即屏幕总宽度。
又因为 所有子View 的 总weight值为2,所以,每份weight可以分到屏幕总宽度的一半。
所以,
第一个TextView的总宽度是:0 + LinearLayout剩余部分的一半(屏幕的一半)
第二个TextView的总宽度是:0 + LinearLayout剩余部分的一半(屏幕的一半)

于是,子View 的 layout_width 置为 0dp,均分了。

4、子View 的 layout_width 置为 match_parent

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="aaaaaaaaaaaaaaa"        android:layout_weight="1"        android:background="#ff0000" />    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="bbb"        android:layout_weight="1"        android:background="#00ff00" /></LinearLayout>

这里写图片描述

可以看到,也可以均分。

这里的实现与 layout_width 置为 0dp 是不同的,虽然效果是一样的,可是过程不同。

因为 所有子View 的 layout_width 都为 match_parent,子View的宽度 = LinearLayout的宽度 = 屏幕宽度

所以,LinearLayout可分配的总宽度为:
自身宽度 - 所有子View的宽度和
= 屏幕宽度 - 屏幕宽度 * 2(乘以2 是因为 2个子View 宽度都为屏幕宽度)
= -屏幕宽度

又因为 所有子View 的 总weight值为2,所以,每份weight可以分到 负的屏幕总宽度的一半。
所以,
第一个TextView的总宽度是:屏幕宽度 + LinearLayout剩余部分的一半(负的屏幕的一半) = 屏幕的一半
第二个TextView的总宽度是:屏幕宽度 + LinearLayout剩余部分的一半(负的屏幕的一半) = 屏幕的一半

于是,子View 的 layout_width 置为 match_parent,均分了。

5、验证子View 的 layout_width 置为 match_parent 与 0dp 的效果

先验证 0dp:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:layout_width="0dp"        android:layout_height="wrap_content"        android:text="aaaaaaaaaaaaaaa"        android:layout_weight="1"        android:background="#ff0000" />    <TextView        android:layout_width="0dp"        android:layout_height="wrap_content"        android:text="bbb"        android:layout_weight="2"        android:background="#00ff00" /></LinearLayout>

这里写图片描述

因为 所有子View 的 width 都为 0dp,所以,LinearLayout可分配的总宽度为全部宽度,即屏幕总宽度。
又因为 所有子View 的 总weight值为3,所以,每份weight可以分到屏幕总宽度的1/3。
所以,
第一个TextView的总宽度是:0 + LinearLayout剩余部分的1/3(屏幕的1/3) * 1(weight = 1) = 屏幕的 1/3
第二个TextView的总宽度是:0 + LinearLayout剩余部分的1/3(屏幕的1/3) * 2(weight = 2) = 屏幕的 2/3

再验证match_parent:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="aaaaaaaaaaaaaaa"        android:layout_weight="1"        android:background="#ff0000" />    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="bbb"        android:layout_weight="2"        android:background="#00ff00" /></LinearLayout>

这里写图片描述

因为 所有子View 的 layout_width 都为 match_parent,子View的宽度 = LinearLayout的宽度 = 屏幕宽度

所以,LinearLayout可分配的总宽度为:
自身宽度 - 所有子View的宽度和
= 屏幕宽度 - 屏幕宽度 * 2(乘以2 是因为 2个子View 宽度都为屏幕宽度)
= -屏幕宽度

又因为 所有子View 的 总weight值为3,所以,每份weight可以分到 负的屏幕总宽度的1/3。
所以,
第一个TextView的总宽度是:屏幕宽度 + LinearLayout剩余部分的1/3(负的屏幕的1/3) * 1(weight = 1) = 屏幕的2/3
第二个TextView的总宽度是:屏幕宽度 + LinearLayout剩余部分的1/3(负的屏幕的1/3) * 2(weight = 2) = 屏幕的1/3

有了以上的例子,想必已经知道 LinearLayout 的 weight 的计算方式了,接下来看看源码中是怎么计算的。

三、源码中 weight 的计算

下面源码只截取了计算 weight 的一小部分,LinearLayout为水平的,计算的是width

        // widthSize 为整个LinearLayout的宽度,mTotalLength 为所有child测量出的宽度和,delta为可分配宽度        int delta = widthSize - mTotalLength;        // totalWeight 是所有child的weight的和        if (delta != 0 && totalWeight > 0.0f) {            // weightSum一般等于totalWeight            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;            ......            mTotalLength = 0;            for (int i = 0; i < count; ++i) {                final View child = getVirtualChildAt(i);                if (child == null || child.getVisibility() == View.GONE) {                    continue;                }                final LinearLayout.LayoutParams lp =                        (LinearLayout.LayoutParams) child.getLayoutParams();                float childExtra = lp.weight;                if (childExtra > 0) {                    // 算出child分配到的宽度                    int share = (int) (childExtra * delta / weightSum);                    // 减去现在的weight                    weightSum -= childExtra;                    // 减去现在的宽度                    delta -= share;                    // 计算子View高度上的MeasureSpec                    final int childHeightMeasureSpec = getChildMeasureSpec(                            heightMeasureSpec,                            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,                            lp.height);                    if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {                        // 如果是 wrap_content 或者 match_parent,lp.width不为0,会执行这里                        // 宽度 = 自身宽度 + 分配宽度                        int childWidth = child.getMeasuredWidth() + share;                        if (childWidth < 0) {                            childWidth = 0;                        }                        child.measure(                            MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),                            childHeightMeasureSpec);                    } else {                        // child的width为0dp时,执行else                        // 宽度 = 分配宽度                        child.measure(MeasureSpec.makeMeasureSpec(                                share > 0 ? share : 0, MeasureSpec.EXACTLY),                                childHeightMeasureSpec);                    }                    childState = combineMeasuredStates(childState,                            child.getMeasuredState() & MEASURED_STATE_MASK);                }                ......            }            ......        }

原创文章,转载请注明出处,谢谢~

0 0
原创粉丝点击