voidmeasureHorizontal(int widthMeasureSpec, int heightMeasureSpec){
// 省略上面部分// ···// See how tall everyone is. Also remember max width.// 遍历测量子view,同时比较出最宽的子view的宽度。for (int i = 0; i < count; ++i) {
// 根据索引返回子view(该方法内直接调用getChildAt,子类可重新该方法扩展,因此有可能返回null)。final View child = getVirtualChildAt(i);
if (child == null) {
// 跳过空的child,measureNullChild固定返回0。
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == View.GONE) {
// 跳过GONE的child,getChildrenSkipCount固定返回0,子类可重写使之跳过后续的child。
i += getChildrenSkipCount(child, i);
continue;
}
// 完成非null和非GONE检查后,计数加一,该计数用于后续divider的判断
nonSkippedChildCount++;
// 首先判断是否计算divider高度// hasDividerBeforeChildAt根据当前索引和showDividers属性值// 判断当前位置是否有divider(可能存在beginning和middle两个位置)。if (hasDividerBeforeChildAt(i)) {
// 若有divider,需要增加divider的高度。
mTotalLength += mDividerHeight;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
// 累加child总权重
totalWeight += lp.weight;
// 标记该child是否使用剩余空间(当child设置高度为0像素且设置大于0的权重,则LinearLayout先分配空间给其他child,// 之后再用剩余空间进行权重分配)。finalboolean useExcessSpace = lp.height == 0 && lp.weight > 0;
if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
// 若高度模式为EXACTLY,表示LinearLayout自身的高度大小是可以确定的(LinearLayout自身的LayoutParams.height// 设置了精确的像素尺寸。或者是MATCH_PARENT,但LinearLayout的父布局的高度也是确定的)。// 且同时该child被标记使用剩余空间,则先不测量和获取child的测量高度,仅计算margin。// Optimization: don't bother measuring children who are only// laid out using excess space. These views will get measured// later if we have space to distribute.// 这里可以优化的原因是因为LinearLayout的LayoutParams.height是可以明确的,也不会是WRAP_CONTENT,// 无需知道child自身内容高度来计算LinearLayout自身高度。而child的LayoutParams.height为0像素,// 完全依赖LinearLayout剩余空间分配权重,若剩余空间为0,则child也不会显示。finalint totalLength = mTotalLength;
// 这里取大值是为了避免margin为负数。
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
// 标记skippedMeasure,用于后续判断补充测量。
skippedMeasure = true;
} else {
// 常规测量流程if (useExcessSpace) {
// The heightMode is either UNSPECIFIED or AT_MOST, and// this child is only laid out using excess space. Measure// using WRAP_CONTENT so that we can find out the view's// optimal height. We'll restore the original height of 0// after measurement.// 进入这个条件的话,高度模式只能是UNSPECIFIED或AT_MOST,且child被标记使用剩余空间。// 在child测量前把LayoutParams.height临时改为WRAP_CONTENT,使child能够测量自身内容需要的高度,测量完成后再改回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).// 已占用的高度,如果截至到当前,都没有child设置大于0的权重,赋值为总内容高度,否则赋值为0。finalint usedHeight = totalWeight == 0 ? mTotalLength : 0;
// 第一次调用child测量。
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
// 获取child测量后的高度。finalint childHeight = child.getMeasuredHeight();
if (useExcessSpace) {
// Restore the original height and record how much space// we've allocated to excess-only children so that we can// match the behavior of EXACTLY measurement.// 恢复LayoutParams.height为0。
lp.height = 0;
// 记录消耗的高度,用于后续计算剩余空间。(为什么这里单独记录消耗高度?是因为下面累加内容总高度时,// 也会加进这个child高度,而这个child实际是需要等待后续有剩余空间后再分配高度,因此后续计算时会加上这个值)
consumedExcessSpace += childHeight;
}
finalint totalLength = mTotalLength;
// 累加总内容高度。
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
// 若设置了measureWithLargestChild属性,记录最大的child高度。(默认不执行,可忽略)if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
}
}
// 省略baselineChildIndex属性逻辑部分,默认不执行,不影响主测量流程。// ···// 调用child测量结束,下面是处理宽度相关的逻辑。// 用于标记该child的测量宽度是否有效。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.// 此时child为MATCH_PARENT需要依赖父布局的宽度,但父布局自身宽度也不确定。所以标记matchWidth、matchWidthLocally为true,// 后面将再次调用child测量。
matchWidth = true;
matchWidthLocally = true;
}
// 计算child的marginfinalint margin = lp.leftMargin + lp.rightMargin;
// child的测量宽度加上marginfinalint measuredWidth = child.getMeasuredWidth() + margin;
// 记录最大的child宽度
maxWidth = Math.max(maxWidth, measuredWidth);
// 合并child的测量状态
childState = combineMeasuredStates(childState, child.getMeasuredState());
// 记录是否所有child的宽度都为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.
*/// 若上面标记该child的测量宽度无效,则仅用margin参与比较。
weightedMaxWidth = Math.max(weightedMaxWidth,
matchWidthLocally ? margin : measuredWidth);
} else {
// 若上面标记该child的测量宽度无效,则仅用margin参与比较。
alternativeMaxWidth = Math.max(alternativeMaxWidth,
matchWidthLocally ? margin : measuredWidth);
}
// getChildrenSkipCount方法内直接返回0,子类可重写使之跳过后续的child。
i += getChildrenSkipCount(child, i);
}
// 省略剩余部分// ···
}
privatevoidforceUniformWidth(int count, int heightMeasureSpec){
// Pretend that the linear layout has an exact size.// 获取LinearLayout自身的宽度,生成宽度规格。int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
MeasureSpec.EXACTLY);
// 遍历子viewfor (int i = 0; i< count; ++i) {
final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
// 测量MATCH_PARENT的childif (lp.width == LayoutParams.MATCH_PARENT) {
// Temporarily force children to reuse their old measured height// FIXME: this may not be right for something like wrapping text?int oldHeight = lp.height;
// 临时修改child的LayoutParams.height为child的测量高度,因为当前child的高度已经测量完成。
lp.height = child.getMeasuredHeight();
// Remeasue with new dimensions// 调用child测量
measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
lp.height = oldHeight;
}
}
}
}
onLayout布局
布局也是根据排列方向执行不同的方法:
@OverrideprotectedvoidonLayout(boolean changed, int l, int t, int r, int b){
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}
layoutVertical和layoutHorizontal布局逻辑相似,这里以垂直排列为例:
voidlayoutVertical(int left, int top, int right, int bottom){
finalint paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// Where right end of child should gofinalint width = right - left;
int childRight = width - mPaddingRight;
// Space available for child// child可用宽度int childSpace = width - paddingLeft - mPaddingRight;
finalint count = getVirtualChildCount();
// 垂直方向对齐方式finalint majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
// 水平方向对齐方式finalint minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
// 根据垂直方向对齐方式,先对上边界做偏移。switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already// 靠底部,上边界需向下偏移,偏移距离即为LinearLayout高度减去内容高度的空白区域高度。
childTop = mPaddingTop + bottom - top - mTotalLength;
break;
// mTotalLength contains the padding alreadycase Gravity.CENTER_VERTICAL:
// 垂直居中,上边界向下偏移空白区域一半的高度。
childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break;
case Gravity.TOP:
default:
// 默认即为靠顶部
childTop = mPaddingTop;
break;
}
// 遍历子view,一次调用child.layoutfor (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} elseif (child.getVisibility() != GONE) {
finalint childWidth = child.getMeasuredWidth();
finalint childHeight = child.getMeasuredHeight();
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
// 获取child设置的LayoutParams的对齐方式int gravity = lp.gravity;
if (gravity < 0) {
// 若没有设置,则以LinearLayout的水平方向对齐方式为准。
gravity = minorGravity;
}
// 获取内容布局方向(RTL或LTR)finalint layoutDirection = getLayoutDirection();
// 转换相对对齐方式(将START、END转换成LEFT、RIGHT)finalint absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
// 处理水平方向对齐switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = paddingLeft + lp.leftMargin;
break;
}
// 判断该索引位置是否有divider,若有,上边界需要偏移DividerHeight。if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
childTop += lp.topMargin;
// setChildFrame方法内直接调用child的layout进行布局。
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
// getChildrenSkipCount方法返回0
i += getChildrenSkipCount(child, i);
}
}
}