Android Layout Tricks #1

The Android UI toolkit offers several layout managers that are rather easy to use and, most of the time, you only need the basic features of these layout managers to implement a user interface. Sticking to the basic features is unfortunately not the most efficient way to create user interfaces. A common example is the abuse of LinearLayout, which leads to a proliferation of views in the view hierarchy. Every view, or worse every layout manager, you add to your application comes at a cost: initialization, layout and drawing become slower. The layout pass can be especially expensive when you nest several LinearLayout that use the weight parameter, which requires the child to be measured twice.

Let’s consider a very simple and common example of a layout: a list item with an icon on the left, a title at the top and an optional description underneath the title. Here is what such an item looks like:

Simple list item

To clearly understand how the views, one ImageView and two TexView, are positioned with respect to each other, here is the wireframe of the layout as captured by HierarchyViewer:

Wireframe of the simple list item

Implementing this layout is straightforward with LinearLayout. The item itself is a horizontal LinearLayout with an ImageView and a vertical LinearLayout, which contains the two TextViews. The source code of this layout is the following:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    
    android:padding="6dip">
    
    <ImageView
        android:id="@+id/icon"
        
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_marginRight="6dip"
        
        android:src="@drawable/icon" />

    <LinearLayout
        android:orientation="vertical"
    
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="fill_parent">

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
                    
            android:gravity="center_vertical"
            android:text="My Application" />
            
        <TextView  
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1" 
            
            android:singleLine="true"
            android:ellipsize="marquee"
            android:text="Simple application that shows how to use RelativeLayout" />
            
    </LinearLayout>

</LinearLayout>

This layout works but can be wasteful if you instantiate it for every list item of a ListView. The same layout can be rewritten using a single RelativeLayout, thus saving one view, and even better one level in view hierarchy, per list item. The implementation of the layout with a RelativeLayout remains simple:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    
    android:padding="6dip">
    
    <ImageView
        android:id="@+id/icon"
        
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:layout_marginRight="6dip"
        
        android:src="@drawable/icon" />

    <TextView  
        android:id="@+id/secondLine"

        android:layout_width="fill_parent"
        android:layout_height="26dip" 
        
        android:layout_toRightOf="@id/icon"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        
        android:singleLine="true"
        android:ellipsize="marquee"
        android:text="Simple application that shows how to use RelativeLayout" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        
        android:layout_toRightOf="@id/icon"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_above="@id/secondLine"
        android:layout_alignWithParentIfMissing="true"
                
        android:gravity="center_vertical"
        android:text="My Application" />

</RelativeLayout>

This new implementation behaves exactly the same way as the previous implementation, except in one case. The list item we want to display has two lines of text: the title and an optional description. When a description is not available for a given list item, the application would simply set the visibility of the second TextView to GONE. This works perfectly with the LinearLayout implementation but not with the RelativeLayout version:

RelativeLayout and description GONE
RelativeLayout and description GONE

In a RelativeLayout, views are aligned either with their parent, the RelativeLayout itself, or other views. For instance, we declared that the description is aligned with the bottom of the RelativeLayout and that the title is positioned above the description and anchored to the parent’s top. With the description GONE, RelativeLayout doesn’t know where to position the title’s bottom edge. To solve this problem, you can use a very special layout parameter called alignWithParentIfMissing.

This boolean parameter simply tells RelativeLayout to use its own edges as anchors when a constraint target is missing. For instance, if you position a view to the right of a GONE view and set alignWithParentIfMissing to true, RelativeLayout will instead anchor the view to its left edge. In our case, using alignWithParentIfMissing will cause RelativeLayout to align the title’s bottom with its own bottom. The result is the following:

RelativeLayout, description GONE and alignWithParentIfMissing
RelativeLayout, description GONE and alignWithParentIfMissing

The behavior of our layout is now perfect, even when the description is GONE. Even better, the hierarchy is simpler and because we are not using LinearLayout’s weights it’s also more efficient. The difference between the two implementations becomes obvious when comparing the view hierarchies in HierarchyViewer:

LinearLayout vs RelativeLayout

Again, the difference will be much more important when you use such a layout for every item in a ListView for instance. Hopefully this simple example showed you that getting to know your layouts is the best way to learn how to optimize your UI.

19 Responses to “Android Layout Tricks #1”

  1. Mariano Kamp says:

    Great stuff. Looking forward to the other tips.

    How would you go about another image on the right side and before that on the first line a TextView TV that is sometimes visible, sometimes gone?
    How would you specify the width of the first line then? I guess that “leftOf” the second image does not work as it is not always the right neighbor as sometimes TV is visible and then TV is the right neighbor. Also as there is the image on the right alignWithParentIfMissing wouldn’t work either, or would it?

    Image | first line | TV (visible/gone)| Image 2
    .. | second line | ..

    Btw. How do you define that Image 2 should span both rows without specifying the height manually?

  2. Mariano Kamp says:

    Oh, the formatting got screwed. I intended to show this: http://pastie.org/397691

  3. Romain Guy says:

    I don’t understand why it was so complicated for you to do this. Just a simple FrameLayout with margins would work fine.

  4. ralfonso says:

    Strangely, this doesn’t work me at all on Cupcake. I’m using a custom adapter and after setting the text values in my getView(), the first TextView somehow ends up with a height of zero and the second TextView gets vertically centered in the RelativeLayout. I changed the names of the IDs, but I copied it right from your post. I wonder what’s up.

  5. Zied Hamdi says:

    Great hint,

    I tried to apply it to a repetitive item which looks exactly as the phone entry in android standard contacts app (label, value, minusButton).

    The problem is that it seams as soon as we declare a field with the attr “fill_parent” it stops looking if there’s somthing on its right.

    I attempted finally to do nested RelativeLayouts as the code shows below. Again, the buttons dosn’t take its place as in a linear layout. Any suggestions please?

  6. Zied Hamdi says:

    sorry the dosn’t show, but the idea is to do a main relative layout with another one inside to allow the two first elements to take all the allowed space, then with a fixed padding right let the space for the thired element. The question is: is it still more performant to have two nested relative layouts rather than one linear layout?

  7. Uberto says:

    It doesn’t work here either, only the second line is visible. Android 1.6 on emulator.

  8. Ivan Frantar says:

    Like @Uberto says. The Second line doesn’t show either on Android 1.6 :( Also, I’ve been using TableLayout for this and the scrolling looks rendering it on a list smoother than using RelativeLayout.

    Thanks for the post!

  9. Aaron B says:

    I tried adding an android:id=”@+id/title” to the “My Application” line and then it stops displaying Text…

    Why is that if there is no ID it works but adding an ID breaks it? that doesn’t make sense

  10. Michael T. says:

    To those with problems with lines not appearing, I had to change the way I was inflating the layout in my Adapter.

    from: myView = layoutInflater.inflate(resID, null);
    to: myView = layoutInflater.inflate(resID, parent, false);

  11. Tim B├╝the says:

    Thanks!

  12. Kevin says:

    Ah! Yes. Thanks Michael T. That was the trick!

  13. Fabian says:

    Thank you for your helpful post.
    I have been trying to create a custom ListView layout for a long time now, but always struggled with simple problems. Thanks to you, I now redesigned my layout and now everything works well – THANK YOU!