Android Layout Tricks #3: Optimize, Part 1

In the previous installment of Android Layout Tricks, I showed you how to use the <include /> tag in XML layout to reuse and share your layout code. I also mentioned the <merge /> and it’s now time to learn how to use it.

The <merge /> was created for the purpose of optimizing Android layouts by reducing the number of levels in view trees. It’s easier to understand the problem this tag solves by looking at an example. The following XML layout declares a layout that shows an image with its title on top of it. The structure is fairly simple; a FrameLayout is used to stack a TextView on top of an ImageView:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <ImageView  
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
    
        android:scaleType="center"
        android:src="@drawable/golden_gate" />
    
    <TextView
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginBottom="20dip"
        android:layout_gravity="center_horizontal|bottom"

        android:padding="12dip"
        
        android:background="#AA000000"
        android:textColor="#ffffffff"
        
        android:text="Golden Gate" />

</FrameLayout>

This layout renders nicely as we expected and nothing seems wrong with this layout:

A FrameLayout is used to overlay a title on top of an image

Things get more interesting when you inspect the result with HierarchyViewer. If you look closely at the resulting tree you will notice that the FrameLayout defined in our XML file (highlighted in blue below) is the sole child of another FrameLayout:

A layout with only one child of same dimensions can be removed

Since our FrameLayout has the same dimension as its parent, by the virtue of using the fill_parent constraints, and does not define any background, extra padding or a gravity, it is totally useless. We only made the UI more complex for no good reason. But how could we get rid of this FrameLayout? After all, XML documents require a root tag and tags in XML layouts always represent view instances.

That’s where the <merge /> tag comes in handy. When the LayoutInflater encounters this tag, it skips it and adds the <merge /> children to the <merge /> parent. Confused? Let’s rewrite our previous XML layout by replacing the FrameLayout with <merge />:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <ImageView  
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
    
        android:scaleType="center"
        android:src="@drawable/golden_gate" />
    
    <TextView
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginBottom="20dip"
        android:layout_gravity="center_horizontal|bottom"

        android:padding="12dip"
        
        android:background="#AA000000"
        android:textColor="#ffffffff"
        
        android:text="Golden Gate" />

</merge>

With this new version, both the TextView and the ImageView will be added directly to the top-level FrameLayout. The result will be visually the same but the view hierarchy is simpler:

Optimized view hierarchy using the merge tag

Obviously, using <merge /> works in this case because the parent of an activity’s content view is always a FrameLayout. You could not apply this trick if your layout was using a LinearLayout as its root tag for instance. The <merge /> can be useful in other situations though. For instance, it works perfectly when combined with the <include /> tag. You can also use <merge /> when you create a custom composite view. Let’s see how we can use this tag to create a new view called OkCancelBar which simply shows two buttons with customizable labels. You can also download the complete source code of this example. Here is the XML used to display this custom view on top of an image:

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge">

    <ImageView  
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
    
        android:scaleType="center"
        android:src="@drawable/golden_gate" />
    
    <com.example.android.merge.OkCancelBar
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom"

        android:paddingTop="8dip"
        android:gravity="center_horizontal"
        
        android:background="#AA000000"
        
        okCancelBar:okLabel="Save"
        okCancelBar:cancelLabel="Don't save" />

</merge>

This new layout produces the following result on a device:

Creating a custom view with the merge tag

The source code of OkCancelBar is very simple because the two buttons are defined in an external XML file, loaded using a LayoutInflate. As you can see in the following snippet, the XML layout R.layout.okcancelbar is inflated with the OkCancelBar as the parent:

public class OkCancelBar extends LinearLayout {
    public OkCancelBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(HORIZONTAL);
        setGravity(Gravity.CENTER);
        setWeightSum(1.0f);
        
        LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);
        
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);
        
        String text = array.getString(R.styleable.OkCancelBar_okLabel);
        if (text == null) text = "Ok";
        ((Button) findViewById(R.id.okcancelbar_ok)).setText(text);
        
        text = array.getString(R.styleable.OkCancelBar_cancelLabel);
        if (text == null) text = "Cancel";
        ((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
        
        array.recycle();
    }
}

The two buttons are defined in the following XML layout. As you can see, we use the <merge /> tag to add the two buttons directly to the OkCancelBar. Each button is included from the same external XML layout file to make them easier to maintain; we simply override their id:

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <include
        layout="@layout/okcancelbar_button"
        android:id="@+id/okcancelbar_ok" />
        
    <include
        layout="@layout/okcancelbar_button"
        android:id="@+id/okcancelbar_cancel" />
</merge>

We have created a flexible and easy to maintain custom view that generates an efficient view hierarchy:

The resulting hierarchy is simple and efficient

The <merge /> tag is extremely useful and can do wonders in your code. However, it suffers from a couple of limitation:

  • <merge /> can only be used as the root tag of an XML layout
  • When inflating a layout starting with a <merge />, you must specify a parent ViewGroup and you must set attachToRoot to true (see the documentation of the inflate() method)

In the next installment of Android Layout Tricks you will learn about ViewStub, a powerful variation of <include /> that can help you further optimize your layouts without sacrificing features.

Download the complete source code of this example.

21 Responses to “Android Layout Tricks #3: Optimize, Part 1”

  1. Sowmya says:

    Hi Thanks for the tips….
    I have some query regarding hierarchy viewer..Can you help me on this?

    I connected my G1 phone and launched the hierarchy viewer. I then selected the device and clicked “Load View Hierarchy” option,the layout didnt get loaded but I got a “java.net.SocketException :Connection Reset”.
    Then i found that the view server was not running,while the device is connected. But it was running with the emulator
    HierarchyViewer worked fine with the emulator,but not with device.

    Alternately I tried the adb shell command “service” to start the view server on the device which too didnt work…

  2. Romain Guy says:

    HierarchyViewer cannot work with production G1s. You need an engineering or custom build to make it work. This is for security reasons (otherwise HierarchyViewer would let you inspect all the UI data, including passwords, of any app of any phone.)

  3. Sowmya says:

    Thanks for the help…

  4. Ddorid says:

    really nice tutorial !

  5. Hey, nice tips. I’ll buy a glass of beer to that person from that chat who told me to visit your site :)

  6. Xylenz says:

    I’m confused [android newb alert]. The second xml snippet is not in MergeLayout.zip. Is that supposed to be okcancelbar.xml? (It would really help if you put the file names on the code snippets so we know what we are looking at.) If so then I get the error:

    No resource found that matches the given name (at ‘layout’ with value ‘@layout/okcancelbar_button’).

    for both of those resources. R.layout defines main, okcancelbar and okcancelbar_button. Dont those all have to exist as xml files in res/layout? There is only a main.xml. Is something missing?

  7. Abhijit Khadilkar says:

    Hi the code given by you is not working properly .
    it throws errors.

  8. Mark says:

    Would be a nice tutorial if it was complete! As Xylenz noted, two .xml files are missing: okcancelbar and okcancelbar_button. Also, the zip includes R.java, but this file should be auto-generated, not included in the project manually.

    While these are simple items to address by an experienced Android developer, they’re serious errors for a tutorial intended to be useful to people that don’t know much about Android development. I’m assuming such a basic tutorial is not intended for the experienced Android developer.

  9. Michael Lam says:

    i tried to run your code on eclipse, but got these build errors:

    Description Resource Path Location Type
    error: No resource identifier found for attribute ‘cancelLabel’ in package ‘com.example.android.merge’ main.xml /MegeActivity/res/layout line 14 Android AAPT Problem
    error: No resource identifier found for attribute ‘cancelLabel’ in package ‘com.example.android.merge’ main.xml /MegeActivity/res/layout line 14 Android AAPT Problem
    error: No resource identifier found for attribute ‘okLabel’ in package ‘com.example.android.merge’ main.xml /MegeActivity/res/layout line 14 Android AAPT Problem

    can someone help me?

  10. Ponder Muse says:

    Hi,

    I am new to android development and I have downloaded the above example’s zip file to try out. However, as others have already mentioned, the zip file is missing a couple of files.

    Could somebody please post these missing files so that the example can be tried out by Android noobs such as me?

    Thank you in advance for your troubles.

    Regards,
    PM.

  11. Fahad says:

    Dude, u are awesome . Thanks for the simple concept. I was looking for this tiny detail all over and it was just so annoying.
    Thanks you so much.

    Keep up the good work !!!

  12. Andrew says:

    I’m having the same problem as some of the users above. Some files are missing from the sample. Any plans to remedy this? I am new to Android, and the fix is not immediately clear.

    Thanks

  13. John says:

    Totally agree with comments on the xml. There appear to be cross-references between two xml files which are un-named. I’ve tried a number of permutations without success. Also the deprecated references to R.stylable. Help us out Romain – please.

  14. This was a good post.. This was my 1st time to this blog. Appreciate you for sharing . I have to subscribe to this website. I was a boat repairman for five years. Our do-it-yourself tip of the day is this: Please do not make an attempt a hard repair job without a professional. That will cost you additional money in the end. Thank you once again…..

  15. There are definitely plenty of particulars like that to take into consideration. That could be a nice point to deliver up. I offer the ideas above as basic inspiration however clearly there are questions like the one you deliver up the place a very powerful factor will probably be working in trustworthy good faith. I don?t know if finest practices have emerged around things like that, but I am certain that your job is clearly identified as a good game. Both boys and girls feel the affect of only a moment抯 pleasure, for the remainder of their lives.