Android – How to write Batman like xml layout

When people talk about Android performance they generally talk about writing a cleaner and faster Java code, while completely ignoring the layout files. Slow layout rendering can have a huge impact on your application’s performance. Layout files with unnecessary views are hard to read and they can slow your application down. In this blog post I will share 5 tips which can help you write efficient and clean xml layout.

1. Use compound drawable on a TextView

Often you need to add an image next to text, and let’s say you want that image to be left of text. Something like this:

Text and image

First thing that comes to mind is to add a Linear or Relative layout and position TextView and ImageView inside that layout. You end up with 3 UI elements and a lot of code. Using a compound drawable on the TextView is a much better and cleaner solution. You can add a drawable next to TextView with only one attribute.

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/batman"
        android:drawableLeft="@drawable/batman"
        android:drawableStart="@drawable/batman"
        android:drawablePadding="5dp">
</TextView>

Key attributes here are:

drawableLeft – Specify the drawable which will be drawn to the left of the text.
drawableStart – The same as drawableLeft but it’s used on newer APIs to support RTL.
drawablePadding – Specify how much padding there will be between text and drawable.

2. ImageView has src and background attribute

And you should use them both. In many cases you want to add press effect on ImageView and I have seen a lot of people wrap ImageView inside LinearLayout only to add press effect. Adding another view is useless. Here is how you can do it better:

    <ImageView
            android:id="@+id/image"
            android:layout_width="@dimen/batman_logo_width"
            android:layout_height="@dimen/batman_logo_height"
            android:background="?attr/selectableItemBackground"
            android:src="@drawable/batman_logo_transparent"
            style="@style/logo_image_style"/>

Image is loaded into ImageView with “src” attribute, and drawable selector, which serves as a press effect is added as “background”. Default android selector is added, but you can add your own if you need that. This is the end result:

3. Use LinearLayout divider

Dividers are commonly used in mobile application and it might surprise you but LinearLayout has an attribute which can help you add dividers. Here is an example of an LinearLayout with two text views and divider between them.

HorizontalDivider

Many programmers would simply add a third View in the middle which would serve as a divider, but that’s useless. With the “android:divider” parameter you can easily accomplish the same. Here is how:

1. Create divider shape

Here is a simple divider_horizontal.xml shape which is used as a divider.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <size android:width="@dimen/divider_width"/>
    <solid android:color="@color/colorPrimaryDark"/>

</shape>

2. Add shape to LinearLayout

    <LinearLayout android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="horizontal"
                  android:background="@android:color/white"
                  android:divider="@drawable/divider_horizontal"
                  android:dividerPadding="5dp"
                  android:showDividers="middle">


        <TextView android:layout_width="0dp"
                  android:layout_weight="0.5"
                  android:layout_height="wrap_content"
                  android:gravity="center"
                  style="@style/Text.Title"
                  android:text="@string/batman_name"/>

        <TextView android:layout_width="0dp"
                  android:layout_height="wrap_content"
                  android:layout_weight="0.5"
                  android:gravity="center"
                  style="@style/Text.Title"
                  android:text="@string/superman_name"/>

    </LinearLayout>

There are three xml attributes to define in this case, and this is what they do:

divider – Used to define a drawable or color which will be used as divider
showDividers – Specify where dividers will be shown. They can be shown at the beginning, in the middle, at the end or you can choose not to show them.
dividerPadding – Allows you to add padding to the layout divider

This is a really simple solution and use it as much as possible.

4. Use the Space view

When you need to add space between two UI elements you probably add padding or margin and sometimes you end up with a layout xml which is so confusing that it’s difficult to read. When you need to fix something, suddenly you realize that there is a 5dp paddingTop here, 2dp marginBottom there, 4dp paddingBottom on the third element and it’s hard to figure out which element is the cause of your problems. Also, I have seen people adding LinearLayout or View to add space between their UI elements and although it seams like an easy way to solve your problem it can have a massive affect on your application’s performance.

There is a much easier and simpler way to add space between your UI elements and, shockingly, it’s called Space. Instead of explaining what it is, here is a copy-paste from Android documentation: “Space is a lightweight View subclass that may be used to create gaps between components in general purpose layouts.” And they are not lying, it’s really lightweight. If you take a look at the Space.java implementation you will see that Space extends View class but it does not draw anything on canvas.

    /**
     * Draw nothing.
     *
     * @param canvas an unused parameter.
     */
    @Override
    public void draw(Canvas canvas) {
    }

It’s really easy to use. Let’s say you want to add space between title and description on this screen.

Space

You simply add Space element between two TextViews.

        <TextView android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  style="@style/Text.Title"

        <Space android:layout_width="match_parent"
               android:layout_height="10dp"/>

        <TextView android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:text="@string/gotham_city_description"
                  style="@style/Text.Details"/>

5. Use <include/> and <merge/>

Reusing layouts is a great way to keep your application consistent. Also, any future changes will be much easier because you’ll only have to change the one instance of the layout. Android offers a tag which helps you reuse your layouts and it’s called <include/>.

Let’s say you decided to create a cool Toolbar with Batman logo in the center and you want to add it to every screen in your app. Here is how cool that toolbar looks like:

Batman toolbar

And here is xml code for batman_toolbar.xml.

    <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/AppTheme.AppBarOverlay"
            app:popupTheme="@style/AppTheme.PopupOverlay">


        <ImageView android:layout_width="wrap_content"
                   android:layout_height="@dimen/batman_logo_height"
                   android:layout_gravity="center"
                   android:src="@drawable/batman_logo_transparent"/>

    </android.support.v7.widget.Toolbar>

You can just copy paste this code in every activity in the app, but don’t do it. There is one important rule in programming: If you’re using copy-paste, you’re doing something wrong. In this case you can use <include/> tag to reuse this layout on multiple screens.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:background="@android:color/white"
        tools:context=".MainActivity">

    <include layout="@layout/batman_toolbar"/>

</android.support.design.widget.CoordinatorLayout>

With <include/> you can add your toolbar to each screen of the app with only one line of code and any change will automatically be propagated to all screens.

Alongside <include/>, <merge/> is commonly used to remove unnecessary Views from your view hierarchy. It removes nested layouts when they are not necessary. For example, if the root of the included layout is a LinearLayout, and you need to include it in another LinearLayout, there is no need for two nested LinearLayouts. The nested layout has no affect on anything, except the UI performance. In that case instead of LinearLayout you can use <merge/> and remove unnecessary View.

You can read more about <include/> and <merge/> in the official Android documentation.

Don’t always play by the rules

I hope that these 5 tips will help you write better and simpler layouts, but you should not think about these tips as rules, more as a guideline. There are always circumstances when you won’t be able to use any of this, and you’ll have to add complexity to your layout. In that case, before adding many views, think about writing your own custom View and try to find the simplest solution possible. Just keep in mind one thing, performance wise, adding a View in your view hierarchy is not cheap and it can impact your application loading speed.

Thanks to Ana and oFca for proofreading this blog post.

  • Great article, I was just thinking these days how to minimize XMLs and speed up loading views 🙂

  • miva2

    Great article, thanks a lot!
    I do disagree with one point. ” If you’re using copy-paste, you’re doing something wrong. ”
    Copy-paste first, then when you see things are too similar, abstract them. Abstracting things too early only makes your code convoluted and keeps you from what you really are trying to achieve. I do agree that repetitive code should be eliminated but for me it’s better to get what you want on the screen first before refactoring.