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><include /></code> tag in XML layouts, to reuse and share your layout code. This article explains the <code><merge /></code> tag and how it complements the <code><include /></code> tag.</p> 5 6 <p>The <code><merge /></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"><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 15 android:layout_width="fill_parent" 16 android:layout_height="fill_parent"> 17 18 <ImageView 19 android:layout_width="fill_parent" 20 android:layout_height="fill_parent" 21 22 android:scaleType="center" 23 android:src="@drawable/golden_gate" /> 24 25 <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" /> 37 38 </FrameLayout></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><merge /></code> tag comes in handy. When the 60 {@link android.view.LayoutInflater} encounters this tag, it skips it and adds 61 the <code><merge /></code> children to the <code><merge /></code> 62 parent. Confused? Let's rewrite our previous XML layout by replacing the 63 <code>FrameLayout</code> with <code><merge /></code>:</p> 64 65 <pre class="prettyprint"><merge xmlns:android="http://schemas.android.com/apk/res/android"> 66 67 <ImageView 68 android:layout_width="fill_parent" 69 android:layout_height="fill_parent" 70 71 android:scaleType="center" 72 android:src="@drawable/golden_gate" /> 73 74 <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" /> 86 87 </merge></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><merge /></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><merge /></code> can be useful in 100 other situations though. For instance, it works perfectly when combined with the 101 <code><include /></code> tag. You can also use <code><merge 102 /></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"><merge 110 xmlns:android="http://schemas.android.com/apk/res/android" 111 xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge"> 112 113 <ImageView 114 android:layout_width="fill_parent" 115 android:layout_height="fill_parent" 116 117 android:scaleType="center" 118 android:src="@drawable/golden_gate" /> 119 120 <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" /> 132 133 </merge></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><merge /></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"><merge xmlns:android="http://schemas.android.com/apk/res/android"> 174 <include 175 layout="@layout/okcancelbar_button" 176 android:id="@+id/okcancelbar_ok" /> 177 178 <include 179 layout="@layout/okcancelbar_button" 180 android:id="@+id/okcancelbar_cancel" /> 181 </merge></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><merge /></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><merge /></code> can only be used as the root tag of an XML layout</li> 193 <li>When inflating a layout starting with a <code><merge /></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