LinearLayout中两个场景的分析:LinearLayout都布局在根布局中,且根布局width为match_parent,LinearLayout 中 orientation = vertical,layout_width = wrap_content
1.LinearLayout它有一个子view,子view layout_width = match_parent
问题:LinearLayout和子view分别显示的宽度是多大?
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#ff00ff">
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#00ff00"
/>
</LinearLayout>
2.LinearLayout有两个子view, 子view1 的layout_width = match_parent , 子view2 的layout_width = 50dp
问题:LinearLayout和子view1、子view2 分别显示的宽度是多大?
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#ff00ff">
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#00ff00"
/>
<View
android:layout_width="50dp"
android:layout_height="100dp"
android:background="#00ffff"
/>
</LinearLayout>
前置知识:
1.View的测量流程,LinearLayout调用onMeasure()会去里面遍历执行子View的measure()
2.测量模式,子view最终的大小,是由子view的width height和父布局的测量模式和剩余空间所决定的
分析过程:
关于最上面的两个问题,都可以在LinearLayout#onMeasure()方法里面找到答案
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
我们选择的是竖直模式,所以查看measureVertical()方法,这里面主要是多次遍历子view进行测量,对于我们上面的两个问题,主要是跟第一次遍历有关系。为了得到显示的宽度,所以我们只关心maxWidth和alternativeMaxWidth相关的代码。
#measureVertical()
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
//...
//这里是对循环中的子view进行测量,只有测量后的子view才能得到实际的宽高
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
//...
boolean matchWidthLocally = false;
//标记1
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;
//每个子view测量后的width
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
childState = combineMeasuredStates(childState, child.getMeasuredState());
//判断是否每个子view都是match_parent模式的
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 {
//标记2
alternativeMaxWidth = Math.max(alternativeMaxWidth,
matchWidthLocally ? margin : measuredWidth);
}
i += getChildrenSkipCount(child, i);
}
以及测量完子view之后的一些逻辑
#measureVertical()
//...
//如果子view全部都是match_parent并且父布局的测量模式不是EXACTLY那么会使用alternativeMaxWidth作为测量宽度
//标记3
if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
maxWidth = alternativeMaxWidth;
}
//添加左右padding
maxWidth += mPaddingLeft + mPaddingRight;
// Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
//把测量得到的width和height进行保存
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
//...
根据上面源码的阅读,就能知道最开始两个题目的答案了。
问题1分析:父布局为wrap_content,只要一个子view,且子view为match_parent。那么这种情况下标记1的判断会是true, matchWidthLocally=true,allFillParent=true,maxWidth是父布局可以得到的最大值。这样就不会进入标记3的判断,所以最终的宽度是maxWidth。
问题2分析:父布局为wrap_content,第一个子view为match_parent,第二个子view为固定宽度。那么,循环到第二个子view时标记1为false,matchWidthLocally=false;标记2中alternativeMaxWidth=measuredWidth;dth也就是子view实际测量值;allFillParent=false 标记3会进入,maxWidth=alternativeMaxWidth。所以这个时候,父布局的宽度就取了第二个子view的实际测量宽度了。