一、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);
}
......
}
......
}
原创文章,转载请注明出处,谢谢~