Home | History | Annotate | Download | only in articles
      1 page.title=Layout Tricks: Merging Layouts
      2 @jd:body
      3 
      4 <p>The <a href=""></a> articles showed you how to use the <code>&lt;include /&gt;</code> tag in XML layouts, to reuse and share your layout code. This article explains the <code>&lt;merge /&gt;</code> tag and how it complements the  <code>&lt;include /&gt;</code> tag.</p>
      5 
      6 <p>The <code>&lt;merge /&gt;</code> tag was created for the purpose of
      7 optimizing Android layouts by reducing the number of levels in view trees. It's
      8 easier to understand the problem this tag solves by looking at an example. The
      9 following XML layout declares a layout that shows an image with its title on top
     10 of it. The structure is fairly simple; a {@link android.widget.FrameLayout} is
     11 used to stack a {@link android.widget.TextView} on top of an 
     12 {@link android.widget.ImageView}:</p>
     13 
     14 <pre class="prettyprint">&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     15     android:layout_width="fill_parent"
     16     android:layout_height="fill_parent"&gt;
     17 
     18     &lt;ImageView  
     19         android:layout_width="fill_parent" 
     20         android:layout_height="fill_parent" 
     21     
     22         android:scaleType="center"
     23         android:src="&#64;drawable/golden_gate" /&gt;
     24     
     25     &lt;TextView
     26         android:layout_width="wrap_content" 
     27         android:layout_height="wrap_content" 
     28         android:layout_marginBottom="20dip"
     29         android:layout_gravity="center_horizontal|bottom"
     30 
     31         android:padding="12dip"
     32         
     33         android:background="#AA000000"
     34         android:textColor="#ffffffff"
     35         
     36         android:text="Golden Gate" /&gt;
     37 
     38 &lt;/FrameLayout&gt;</pre>
     39 
     40 <p>This layout renders nicely and nothing seems wrong with it:</p>
     41 
     42 <div style="text-align: center;"><img src="images/merge1.jpg" alt="A FrameLayout is used to overlay a title on top of an image"></div>
     43 
     44 <p>Things get more interesting when you inspect the result with <a
     45 href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">HierarchyViewer</a>. 
     46 If you look closely at the resulting tree, you will notice that the
     47 <code>FrameLayout</code> defined in our XML file (highlighted in blue below) is
     48 the sole child of another <code>FrameLayout</code>:</p>
     49 
     50 <div style="text-align: center;"><img src="images/merge2.png" alt="A layout with only one child of same dimensions can be removed"></div>
     51 
     52 <p>Since our <code>FrameLayout</code> has the same dimension as its parent, by
     53 the virtue of using the <code>fill_parent</code> constraints, and does not
     54 define any background, extra padding or a gravity, it is <em>totally
     55 useless</em>. We only made the UI more complex for no good reason. But how could
     56 we get rid of this <code>FrameLayout</code>? After all, XML documents require a
     57 root tag and tags in XML layouts always represent view instances.</p>
     58 
     59 <p>That's where the <code>&lt;merge /&gt;</code> tag comes in handy. When the
     60 {@link android.view.LayoutInflater} encounters this tag, it skips it and adds
     61 the <code>&lt;merge /&gt;</code> children to the <code>&lt;merge /&gt;</code>
     62 parent. Confused? Let's rewrite our previous XML layout by replacing the
     63 <code>FrameLayout</code> with <code>&lt;merge /&gt;</code>:</p>
     64 
     65 <pre class="prettyprint">&lt;merge xmlns:android="http://schemas.android.com/apk/res/android"&gt;
     66 
     67     &lt;ImageView  
     68         android:layout_width="fill_parent" 
     69         android:layout_height="fill_parent" 
     70     
     71         android:scaleType="center"
     72         android:src="&#64;drawable/golden_gate" /&gt;
     73     
     74     &lt;TextView
     75         android:layout_width="wrap_content" 
     76         android:layout_height="wrap_content" 
     77         android:layout_marginBottom="20dip"
     78         android:layout_gravity="center_horizontal|bottom"
     79 
     80         android:padding="12dip"
     81         
     82         android:background="#AA000000"
     83         android:textColor="#ffffffff"
     84         
     85         android:text="Golden Gate" /&gt;
     86 
     87 &lt;/merge&gt;</pre>
     88 
     89 <p>With this new version, both the <code>TextView</code> and the
     90 <code>ImageView</code> will be added directly to the top-level
     91 <code>FrameLayout</code>. The result will be visually the same but the view
     92 hierarchy is simpler:</p>
     93 
     94 <div style="text-align: center;"><img src="images/merge3.png" alt="Optimized view hierarchy using the merge tag"></div>
     95 
     96 <p>Obviously, using <code>&lt;merge /&gt;</code> works in this case because the
     97 parent of an activity's content view is always a <code>FrameLayout</code>. You
     98 could not apply this trick if your layout was using a <code>LinearLayout</code>
     99 as its root tag for instance. The <code>&lt;merge /&gt;</code> can be useful in
    100 other situations though. For instance, it works perfectly when combined with the
    101 <code>&lt;include /&gt;</code> tag. You can also use <code>&lt;merge
    102 /&gt;</code> when you create a custom composite view. Let's see how we can use
    103 this tag to create a new view called <code>OkCancelBar</code> which simply shows
    104 two buttons with customizable labels. You can also <a
    105 href="http://progx.org/users/Gfx/android/MergeLayout.zip">download the complete
    106 source code of this example</a>. Here is the XML used to display this custom
    107 view on top of an image:</p>
    108 
    109 <pre class="prettyprint">&lt;merge
    110     xmlns:android="http://schemas.android.com/apk/res/android"
    111     xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge"&gt;
    112 
    113     &lt;ImageView  
    114         android:layout_width="fill_parent" 
    115         android:layout_height="fill_parent" 
    116     
    117         android:scaleType="center"
    118         android:src="&#64;drawable/golden_gate" /&gt;
    119     
    120     &lt;com.example.android.merge.OkCancelBar
    121         android:layout_width="fill_parent" 
    122         android:layout_height="wrap_content" 
    123         android:layout_gravity="bottom"
    124 
    125         android:paddingTop="8dip"
    126         android:gravity="center_horizontal"
    127         
    128         android:background="#AA000000"
    129         
    130         okCancelBar:okLabel="Save"
    131         okCancelBar:cancelLabel="Don't save" /&gt;
    132 
    133 &lt;/merge&gt;</pre>
    134 
    135 <p>This new layout produces the following result on a device:</p>
    136 
    137 <div style="text-align: center;"><img src="images/merge4.jpg" alt="Creating a custom view with the merge tag"></div>
    138 
    139 <p>The source code of <code>OkCancelBar</code> is very simple because the two
    140 buttons are defined in an external XML file, loaded using a
    141 <code>LayoutInflate</code>. As you can see in the following snippet, the XML
    142 layout <code>R.layout.okcancelbar</code> is inflated with the
    143 <code>OkCancelBar</code> as the parent:</p>
    144 
    145 <pre class="prettyprint">public class OkCancelBar extends LinearLayout {
    146     public OkCancelBar(Context context, AttributeSet attrs) {
    147         super(context, attrs);
    148         setOrientation(HORIZONTAL);
    149         setGravity(Gravity.CENTER);
    150         setWeightSum(1.0f);
    151         
    152         LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);
    153         
    154         TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);
    155         
    156         String text = array.getString(R.styleable.OkCancelBar_okLabel);
    157         if (text == null) text = "Ok";
    158         ((Button) findViewById(R.id.okcancelbar_ok)).setText(text);
    159         
    160         text = array.getString(R.styleable.OkCancelBar_cancelLabel);
    161         if (text == null) text = "Cancel";
    162         ((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
    163         
    164         array.recycle();
    165     }
    166 }</pre>
    167 
    168 <p>The two buttons are defined in the following XML layout. As you can see, we
    169 use the <code>&lt;merge /&gt;</code> tag to add the two buttons directly to the
    170 <code>OkCancelBar</code>. Each button is included from the same external XML
    171 layout file to make them easier to maintain; we simply override their id:</p>
    172 
    173 <pre class="prettyprint">&lt;merge xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    174     &lt;include
    175         layout="&#64;layout/okcancelbar_button"
    176         android:id="&#64;+id/okcancelbar_ok" /&gt;
    177         
    178     &lt;include
    179         layout="&#64;layout/okcancelbar_button"
    180         android:id="&#64;+id/okcancelbar_cancel" /&gt;
    181 &lt;/merge&gt;</pre>
    182 
    183 <p>We have created a flexible and easy to maintain custom view that generates 
    184 an efficient view hierarchy:</p>
    185 
    186 <div style="text-align: center;"><img src="images/merge5.png" alt="The resulting hierarchy is simple and efficient"></div>
    187 
    188 <p>The <code>&lt;merge /&gt;</code> tag is extremely useful and can do wonders
    189 in your code. However, it suffers from a couple of limitations:</p>
    190 
    191 <ul>
    192 <li><code>&lt;merge /&gt;</code> can only be used as the root tag of an XML layout</li>
    193 <li>When inflating a layout starting with a <code>&lt;merge /&gt;</code>, you <strong>must</strong> 
    194 specify a parent <code>ViewGroup</code> and you must set <code>attachToRoot</code> to 
    195 <code>true</code> (see the documentation for 
    196 {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} method)</li>
    197 </ul>
    198 
    199