android - LinearLayout(baselineAligned)
今天做一个UI突然发现一个奇怪的问题:

第一个textView我确实设置了layout_gravity = bottom,并且其父布局LinearLayout也是horizontal方向。但是1dp的textView并没有居于LinearLayout的底部。
开始分析
一、将TextView先换成View看下效果:

可以看到1dp的view居于LinearLayout的底部了。
二、将LinearLayout换成FrameLayout看下效果:

可以看到1dp的TextView居于LinearLayout的底部了。
三、源码分析
通过前2步的测试,那么可以确定的是,TextView是具有文字内容的控件,我猜测是baseline这个属性影响的。但是textView影响的是LinearLayout,所以我直接去看LinearLayout的源码:
/**
* Position the children during a layout pass if the orientation of this
* LinearLayout is set to {@link #HORIZONTAL}.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
* @param left
* @param top
* @param right
* @param bottom
*/
void layoutHorizontal(int left, int top, int right, int bottom) {
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
int childTop;
int childLeft;
// Where bottom of child should go
final int height = bottom - top;
int childBottom = height - mPaddingBottom;
// Space available for child
int childSpace = height - paddingTop - mPaddingBottom;
final int count = getVirtualChildCount();
final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final boolean baselineAligned = mBaselineAligned;
final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;
final int layoutDirection = getLayoutDirection();
switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
case Gravity.RIGHT:
// mTotalLength contains the padding already
childLeft = mPaddingLeft + right - left - mTotalLength;
break;
case Gravity.CENTER_HORIZONTAL:
// mTotalLength contains the padding already
childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
break;
case Gravity.LEFT:
default:
childLeft = mPaddingLeft;
break;
}
int start = 0;
int dir = 1;
//In case of RTL, start drawing from the last child.
if (isLayoutRtl) {
start = count - 1;
dir = -1;
}
for (int i = 0; i < count; i++) {
final int childIndex = start + dir * i;
final View child = getVirtualChildAt(childIndex);
if (child == null) {
childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int childBaseline = -1;
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
childBaseline = child.getBaseline();
}
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = paddingTop + lp.topMargin;
if (childBaseline != -1) {
childTop += maxAscent[INDEX_TOP] - childBaseline;
}
break;
case Gravity.CENTER_VERTICAL:
// Removed support for baseline alignment when layout_gravity or
// gravity == center_vertical. See bug #1038483.
// Keep the code around if we need to re-enable this feature
// if (childBaseline != -1) {
// // Align baselines vertically only if the child is smaller than us
// if (childSpace - childHeight > 0) {
// childTop = paddingTop + (childSpace / 2) - childBaseline;
// } else {
// childTop = paddingTop + (childSpace - childHeight) / 2;
// }
// } else {
childTop = paddingTop + ((childSpace - childHeight) / 2)
+ lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = childBottom - childHeight - lp.bottomMargin;
if (childBaseline != -1) {
int descent = child.getMeasuredHeight() - childBaseline;
childTop -= (maxDescent[INDEX_BOTTOM] - descent);
}
break;
default:
childTop = paddingTop;
break;
}
if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}
childLeft += lp.leftMargin;
setChildFrame(child, childLeft + getLocationOffset(child), childTop,
childWidth, childHeight);
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);
i += getChildrenSkipCount(child, childIndex);
}
}
}
通过源码可以定位到下面这个case:
case Gravity.BOTTOM:
childTop = childBottom - childHeight - lp.bottomMargin;
if (childBaseline != -1) {
int descent = child.getMeasuredHeight() - childBaseline;
childTop -= (maxDescent[INDEX_BOTTOM] - descent);
}
break;
确定了确实是baseline影响的。
那么LinearLayout 有2个属性 baselineAligned和baselineAlignedChildIndex
如果LinearLayout的orientation为horizontal的话,其中的文字默认是文字对齐的,baselineAligned = true
baselineAlignedChildIndex是指其中第几个元素需要baseline对其。
这里再举一个列子:

此时baselineAligned默认为true,两个textView的文字是底部对其的。
当我把baselineAligned设置为false时:

此时整个TextView控件就居于底部了。