Android:都是Layout的BaselineAligned惹的祸

此问题来自一个网友的提问

看下面的布局文件

<LinearLayout xmlns:android=""xmlns:tools=""android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity" ><TextViewandroid:layout_width="150dp"android:layout_height="60dp"android:background="#8f8f8f"android:text="第一个" /><TextViewandroid: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布局时都干了啥: @Overrideprotected 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是咋样的 @Overridepublic 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。

版权声明:本文为博主原创文章,未经博主允许不得转载。

收敛自己的脾气,偶尔要刻意沉默,

Android:都是Layout的BaselineAligned惹的祸

相关文章:

你感兴趣的文章:

标签云: