Android:都是Layout的BaselineAligned惹的祸
source link: http://www.androidchina.net/3568.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Android:都是Layout的BaselineAligned惹的祸 – Android开发中文站
此问题来自一个网友的提问http://ask.csdn.net/questions/206909#answer_140060
看下面的布局文件
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
tools:context
=
".MainActivity"
>
<
TextView
android:layout_width
=
"150dp"
android:layout_height
=
"60dp"
android:background
=
"#8f8f8f"
android:text
=
"第一个"
/>
<
TextView
android:layout_width
=
"100dp"
android:layout_height
=
"60dp"
android:background
=
"#8f00"
android:gravity
=
"center"
android:text
=
"第二个"
android:textSize
=
"20dp"
/>
</
LinearLayout
>
这个布局非常简单,LinearLayout里面嵌套了两个TextView组件,他们的高度一样,宽度不一样,期望结果是,两个TextView顶端是对齐的,高度一样,大小,文字对齐方式可以任意设置。这个布局实际渲染出来的结构是这样
完全错位了,只要你设置不同的字号,Gravity,都会引起位置错乱,究其原因,就是LinearLayout的BaselineAligned属性惹的祸,看一下LinearLayout的默认属性
很多时候,我们都被那个绿色框框给迷惑了,对于boolean属性,这个状态不一定是false,它只代表与系统设定的默认值一致,那么系统对这个变量是怎样设置的呢,看看LinearLayout源码(位置:\sdks\sources\android-19\android\widget)
/**
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
@ViewDebug
.ExportedProperty(category =
"layout"
)
private
boolean
mBaselineAligned =
true
;
看到了吧,默认值就是true,而且告诉我们只对水平布局有效。那么这个属性对布局产生了怎样的影响呢,继续看LinearLayout布局时都干了啥:
@Override
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
if
(mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
}
else
{
layoutHorizontal(l, t, r, b);
}
}
横向布局时,会调用layoutHorizontal,下面摘取一段代码
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);
}
我们看到在else…if代码段里,出现了baselineAligned的身影:
if
(baselineAligned && lp.height != LayoutParams.MATCH_PARENT)
如果是基线对齐,并且布局参数的高度设定的不是match_parent,那么就要获取chile的BaseLine,这里也就是TextView的BaseLine的值。经过一些列的计算,最后通过
setChildFrame(child, childLeft + getLocationOffset(child), childTop,
childWidth, childHeight);
对child进行OnLayout布局
private
void
setChildFrame(View child,
int
left,
int
top,
int
width,
int
height) {
child.layout(left, top, left + width, top + height);
}
再来看看TextView的getBaseline是咋样的
@Override
public
int
getBaseline() {
if
(mLayout ==
null
) {
return
super
.getBaseline();
}
int
voffset =
0
;
if
((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
voffset = getVerticalOffset(
true
);
}
if
(isLayoutModeOptical(mParent)) {
voffset -= getOpticalInsets().top;
}
return
getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(
0
);
}
如果不是Gravity.TOP,就要通过getVerticalOffset计算voffset值了
int
getVerticalOffset(
boolean
forceNormal) {
int
voffset =
0
;
final
int
gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
Layout l = mLayout;
if
(!forceNormal && mText.length() ==
0
&& mHintLayout !=
null
) {
l = mHintLayout;
}
if
(gravity != Gravity.TOP) {
int
boxht = getBoxHeight(l);
int
textht = l.getHeight();
if
(textht < boxht) {
if
(gravity == Gravity.BOTTOM)
voffset = boxht - textht;
else
// (gravity == Gravity.CENTER_VERTICAL)
voffset = (boxht - textht) >>
1
;
}
}
return
voffset;
}
这个方法会计算一个垂直方向的偏移值,就是这个偏移值,直接影响了Layout布局中的childTop值,导致布局混乱,基线对齐,就是多个TextView的文字是保持对齐的,才不管你top和bottom。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK