Watch That Baseline Alignment

原文

Let’s go out on a limb and say that every Android developer at one point or another uses LinearLayout to build a row of elements.

Let’s take the following simple layout as an example of what I mean. A horizontal row of buttons all evenly spaced out across the container using weight.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Last" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Next" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Reload" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Exit" />
</LinearLayout>

这里写图片描述
The weight system of LinearLayout makes using it to build rows with even spacing across the children simple and effective. However, as often as you may have done this, you might have also run into a perplexing issue that arises when the text of one or more elements inside the row gets long enough to force line wrapping.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Last" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Next" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Reload Content" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Exit" />
</LinearLayout>

这里写图片描述
Seeing this is often followed by…

(╯°□°) ╯︵ ┻━┻

…well, at least in my case. The first time you see this, it is a bit maddening because there is no apparent reason for what has just happened. If you’re like me, you’ll go through your mental checklist:

Did I miss a margin somewhere?
Is there extra padding in the view or the background image?
Does the view add extra padding to accommodate for multiple lines of text?
Why does it only happen to the view when the text wraps?
Not only has the view content been shifted down, but the parent container isn’t sized to fit the larger height this creates, so part of the shifted view is clipped! Also notice that the view is shifted even in cases where the multiple lines don’t make the view inherently taller. This behavior makes absolutely no sense, until you understand what is actually going on under the hood. If we take a much closer look at the alignment of the child elements, something interesting emerges.
这里写图片描述
Eureka! The views are being laid out such that the first line of text is vertically aligned for each child. This is known as “baseline” alignment, and it just so happens LinearLayout has a flag to control this, and it’s enabled by default. In other words, LinearLayout (and its subclasses) will attempt to align child elements by their text baseline when possible. Child elements subject to this are those that report a valid baseline; basically all the subclasses of TextView, but it could be any View that overrides and returns a positive value from getBaseline(). Because of this, you may encounter this behavior for any container widget that is built from LinearLayout (which includes TableLayout, RadioGroup, and SearchView) and child element based on TextView (Button, CheckBox, RadioButton, EditText, just to name a few). More often than not, I hit this condition in my own code building a grid of checkable boxes or a row of radio buttons. ##Solution In some cases, depending on your container height sizing, the solution can simply be to enforce centered gravity on the child elements by adding android:gravity=”center_vertical” to the layout container. Although to some degree, even in cases where this works it is a case of solving the symptom rather than the cause. In this author’s humble opinion, a better solution is to disable baseline alignment for the container view when it is not useful for your application. This can be done in XML with android:baselineAligned=”false” or in Java code via setBaselineAligned(false). Here is our previous example with the fix applied.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_baselineAligned="false"
    android:orientation="horizontal">
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Last" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Next" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Reload Content" />
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="12dp"
        android:text="Exit" />
</LinearLayout>

这里写图片描述
Hopefully this tip will save you from the many facepalms that my forehead was forced to endure. Like solving problems like these? Interested in an Android development position at Double Encore? We’re currently hiring new engineers for our Denver team!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值