Working with Baselines in RelativeLayout

On initial glance, the layout_alignBaseline attribute that can be added to children of a RelativeLayout seems like a handy little tool that is used just as easily as the more common layout_alignTop and layout_alignBottom. The good news is that if you’ve only got a couple of views, it works great! The not so good news is that if your layout gets just a bit more complicated, things fall apart in a hurry.

Let’s say you have a nice design that you need to implement:

The desired result.

I’m not here to argue about what layout is the absolute best for this situation. But, for argument’s sake, let’s say you decide you can easily build this with a RelativeLayout with three children. Nice, flat hierarchy. You might end up with something like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333"
    android:padding="16dp" >

    <TextView
        android:id="@+id/code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/mi"
        android:textColor="#f3f3f3"
        android:textSize="96sp" />

    <TextView
        android:id="@+id/country"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@id/code"
        android:layout_marginLeft="16dp"
        android:layout_toRightOf="@id/code"
        android:text="@string/usa"
        android:textColor="#f3f3f3"
        android:textSize="22sp" />

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@id/country"
        android:layout_above="@id/country"
        android:text="@string/michigan"
        android:textColor="#f3f3f3"
        android:textSize="22sp" />

</RelativeLayout>

So, “United States of America” lines its baseline up with the baseline of “MI” and then “Michigan” sits on top. Perfect. But then you look at the rendered output of that layout:

RelativeLayout fails challenge.

RelativeLayout fails challenge.

So, what went wrong? Did you mess up one of the layout attributes? Turns out it is actually a limitation of RelativeLayout. If you dig into the source (line 519), you will notice that baseline alignment is performed after all other vertical alignment has already been performed. In other words, “Michigan” is lined up above “United States of America” when it is still in its default position at the top of the RelativeLayout container. Then, later on, “United States of America” is re-aligned with the baseline of “MI,” leaving “Michigan” just out of view, above the top of the RelativeLayout.

One potential solution to this problem is to wrap the two smaller TextViews in a LinearLayout that can then be properly baseline aligned with the larger TextView on the left. For example:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333"
    android:padding="16dp" >

    <TextView
        android:id="@+id/code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/mi"
        android:textColor="#f3f3f3"
        android:textSize="96sp" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@id/code"
        android:layout_marginLeft="16dp"
        android:layout_toRightOf="@id/code"
        android:baselineAlignedChildIndex="1"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/michigan"
            android:textColor="#f3f3f3"
            android:textSize="22sp" />

        <TextView
            android:id="@+id/country"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/usa"
            android:textColor="#f3f3f3"
            android:textSize="22sp" />
    </LinearLayout>

</RelativeLayout>

Voila! We get the desired result at the expense of an extra level in the View hierarchy. Oh, and did you notice that neat, relatively rarely used attribute on the LinearLayout? In case you missed it, baselineAlignedChildIndex is a cool little trick to tell the LinearLayout which of its children should be considered when lining up the entire LinearLayout with another View. Very handy!

Leave a Reply

Your email address will not be published. Required fields are marked *

Please Do the Math