201 lines
8.6 KiB
Plaintext
201 lines
8.6 KiB
Plaintext
page.title=Layout Tricks: Merging Layouts
|
|
@jd:body
|
|
|
|
<p>The 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>
|
|
|
|
<p>The <code><merge /></code> tag 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 {@link android.widget.FrameLayout} is
|
|
used to stack a {@link android.widget.TextView} on top of an
|
|
{@link android.widget.ImageView}:</p>
|
|
|
|
<pre class="prettyprint"><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></pre>
|
|
|
|
<p>This layout renders nicely and nothing seems wrong with it:</p>
|
|
|
|
<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>
|
|
|
|
<p>Things get more interesting when you inspect the result with <a
|
|
href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">HierarchyViewer</a>.
|
|
If you look closely at the resulting tree, you will notice that the
|
|
<code>FrameLayout</code> defined in our XML file (highlighted in blue below) is
|
|
the sole child of another <code>FrameLayout</code>:</p>
|
|
|
|
<div style="text-align: center;"><img src="images/merge2.png" alt="A layout with only one child of same dimensions can be removed"></div>
|
|
|
|
<p>Since our <code>FrameLayout</code> has the same dimension as its parent, by
|
|
the virtue of using the <code>fill_parent</code> constraints, and does not
|
|
define any background, extra padding or a gravity, it is <em>totally
|
|
useless</em>. We only made the UI more complex for no good reason. But how could
|
|
we get rid of this <code>FrameLayout</code>? After all, XML documents require a
|
|
root tag and tags in XML layouts always represent view instances.</p>
|
|
|
|
<p>That's where the <code><merge /></code> tag comes in handy. When the
|
|
{@link android.view.LayoutInflater} encounters this tag, it skips it and adds
|
|
the <code><merge /></code> children to the <code><merge /></code>
|
|
parent. Confused? Let's rewrite our previous XML layout by replacing the
|
|
<code>FrameLayout</code> with <code><merge /></code>:</p>
|
|
|
|
<pre class="prettyprint"><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></pre>
|
|
|
|
<p>With this new version, both the <code>TextView</code> and the
|
|
<code>ImageView</code> will be added directly to the top-level
|
|
<code>FrameLayout</code>. The result will be visually the same but the view
|
|
hierarchy is simpler:</p>
|
|
|
|
<div style="text-align: center;"><img src="images/merge3.png" alt="Optimized view hierarchy using the merge tag"></div>
|
|
|
|
<p>Obviously, using <code><merge /></code> works in this case because the
|
|
parent of an activity's content view is always a <code>FrameLayout</code>. You
|
|
could not apply this trick if your layout was using a <code>LinearLayout</code>
|
|
as its root tag for instance. The <code><merge /></code> can be useful in
|
|
other situations though. For instance, it works perfectly when combined with the
|
|
<code><include /></code> tag. You can also use <code><merge
|
|
/></code> when you create a custom composite view. Let's see how we can use
|
|
this tag to create a new view called <code>OkCancelBar</code> which simply shows
|
|
two buttons with customizable labels. You can also <a
|
|
href="http://progx.org/users/Gfx/android/MergeLayout.zip">download the complete
|
|
source code of this example</a>. Here is the XML used to display this custom
|
|
view on top of an image:</p>
|
|
|
|
<pre class="prettyprint"><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></pre>
|
|
|
|
<p>This new layout produces the following result on a device:</p>
|
|
|
|
<div style="text-align: center;"><img src="images/merge4.jpg" alt="Creating a custom view with the merge tag"></div>
|
|
|
|
<p>The source code of <code>OkCancelBar</code> is very simple because the two
|
|
buttons are defined in an external XML file, loaded using a
|
|
<code>LayoutInflate</code>. As you can see in the following snippet, the XML
|
|
layout <code>R.layout.okcancelbar</code> is inflated with the
|
|
<code>OkCancelBar</code> as the parent:</p>
|
|
|
|
<pre class="prettyprint">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();
|
|
}
|
|
}</pre>
|
|
|
|
<p>The two buttons are defined in the following XML layout. As you can see, we
|
|
use the <code><merge /></code> tag to add the two buttons directly to the
|
|
<code>OkCancelBar</code>. Each button is included from the same external XML
|
|
layout file to make them easier to maintain; we simply override their id:</p>
|
|
|
|
<pre class="prettyprint"><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></pre>
|
|
|
|
<p>We have created a flexible and easy to maintain custom view that generates
|
|
an efficient view hierarchy:</p>
|
|
|
|
<div style="text-align: center;"><img src="images/merge5.png" alt="The resulting hierarchy is simple and efficient"></div>
|
|
|
|
<p>The <code><merge /></code> tag is extremely useful and can do wonders
|
|
in your code. However, it suffers from a couple of limitations:</p>
|
|
|
|
<ul>
|
|
<li><code><merge /></code> can only be used as the root tag of an XML layout</li>
|
|
<li>When inflating a layout starting with a <code><merge /></code>, you <strong>must</strong>
|
|
specify a parent <code>ViewGroup</code> and you must set <code>attachToRoot</code> to
|
|
<code>true</code> (see the documentation for
|
|
{@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} method)</li>
|
|
</ul>
|
|
|