Android:都是Layout的BaselineAligned惹的祸
来源:互联网 发布:网络运营商无服务 编辑:程序博客网 时间:2024/05/22 09:05
此问题来自一个网友的提问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顶端是对齐的,高度一样,大小,文字对齐方式可以任意设置。这个布局实际渲染出来的结构是这样
完全错位了,只要你设置不同的字号,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。
1 0
- Android:都是Layout的BaselineAligned惹的祸
- 随笔:Android中的LinearLayout的baselineAligned属性
- LinearLayout中的属性baselineAligned的使用
- LinearLayout中的属性baselineAligned的使用
- LinearLayout属性baselineAligned的作用及baseline
- LinearLayout中的属性baselineAligned的使用
- LinearLayout中的属性baselineAligned的使用
- 都是‘/’惹的祸
- Android 布局学习之——LinearLayout属性baselineAligned的作用及baseline_抄来的
- Android 布局学习之——LinearLayout属性baselineAligned的作用及baseline
- Android 布局LinearLayout——属性baselineAligned的作用及baseline
- Android 布局学习之——LinearLayout属性baselineAligned的作用及baseline
- android:baselineAligned,baselineAlignedChildIndex,addStatesFromChildren
- android:baselineAligned="false"
- Android LineaLayout baselineAligned
- android:baselineAligned=“false”
- 在一个layout中保持button都是一样大小的
- Android的Layout整理
- leetcode Valid Palindrome
- inverse = “true” example and explanation
- EXT结合POI导出数据到Excel表格
- MyBatis学习总结(三)——优化MyBatis配置文件中的配置
- linux下用vim编写C/C++工程(多个源文件)的基本方法
- Android:都是Layout的BaselineAligned惹的祸
- js解决浏览器自动填充账号密码
- 《程序员面试金典》猫狗收容所
- gallery 各种事件监听方法详解
- UGUI之slider详解
- 集合的全排列问题-递归实现方式的改进---旋转法
- 使用SQLAzureMW进行迁移
- 使用JQUERY实现JSON数据三级联动
- WifiManager 常量