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 letters "MI" are large and on the left side. To the right, in a smaller font size, the word "Michigan" sits above the words "United States of America"

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:

The letters "MI" are large on the left side. To the right and in a smaller font size, the words "United States of America" appear lined up with the bottom of the larger "MI" on the left.

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 Comment

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s