转载

Android:都是Layout的BaselineAligned惹的祸

此问题来自一个网友的提问 http://ask.csdn.net/questions/206909#answer_140060

看下面的布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools"  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顶端是对齐的,高度一样,大小,文字对齐方式可以任意设置。这个布局实际渲染出来的结构是这样

Android:都是Layout的BaselineAligned惹的祸

完全错位了,只要你设置不同的字号,Gravity,都会引起位置错乱,究其原因,就是LinearLayout的BaselineAligned属性惹的祸,看一下LinearLayout的默认属性

Android:都是Layout的BaselineAligned惹的祸

很多时候,我们都被那个绿色框框给迷惑了,对于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布局

再来看看TextView的getBaseline是咋样的

如果不是Gravity.TOP,就要通过getVerticalOffset计算voffset值了

这个方法会计算一个垂直方向的偏移值,就是这个偏移值,直接影响了Layout布局中的childTop值,导致布局混乱,基线对齐,就是多个TextView的文字是保持对齐的,才不管你top和bottom。

正文到此结束
Loading...