Layout-Tricks: Merging Layouts

In den Artikeln wurde gezeigt, wie man das <include />-Tag in XML-Layouts verwendet, um den Layout-Code wiederzuverwenden und weiterzugeben. Dieser Artikel erklärt das <merge />-Tag und wie es das <include />-Tag ergänzt.

Das <merge />-Tag wurde entwickelt, um Android-Layouts zu optimieren, indem die Anzahl der Ebenen in Ansichtsbäumen reduziert wird. Es ist einfacher, das Problem, das dieses Tag löst, anhand eines Beispiels zu verstehen. Das folgende XML-Layout deklariert ein Layout, das ein Bild mit seinem Titel darüber anzeigt. Die Struktur ist recht einfach; ein FrameLayout wird verwendet, um ein TextView über ein ImageView zu stapeln:

<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>

Dieses Layout wird gut gerendert und es scheint nichts falsch zu sein:

Ein FrameLayout wird verwendet, um einen Titel über ein Bild zu legen

Interessanter wird es, wenn man das Ergebnis mit dem HierarchyViewer untersucht. Wenn Sie sich den resultierenden Baum genau ansehen, werden Sie feststellen, dass das in unserer XML-Datei definierte FrameLayout (unten blau hervorgehoben) das einzige Kind eines anderen FrameLayout ist:

Ein Layout mit nur einem Kind der gleichen Dimensionen kann entfernt werden

Da unser FrameLayout durch die Verwendung der fill_parent-Einschränkungen die gleiche Dimension wie sein Elternteil hat und keinen Hintergrund, kein zusätzliches Padding oder eine Schwerkraft definiert, ist es völlig nutzlos. Wir haben die Benutzeroberfläche ohne guten Grund nur noch komplexer gemacht. Aber wie könnten wir dieses FrameLayout loswerden? Schließlich benötigen XML-Dokumente einen Root-Tag, und Tags in XML-Layouts stellen immer Ansichtsinstanzen dar.

Da kommt der <merge />-Tag gerade recht. Wenn LayoutInflater auf dieses Tag trifft, überspringt es es und fügt die <merge />-Kinder zum <merge />-Elternteil hinzu. Verwirrt? Schreiben wir unser vorheriges XML-Layout neu, indem wir dasFrameLayout durch <merge /> ersetzen:

<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>

Mit dieser neuen Version werden sowohl das TextView als auch dasImageView direkt dem übergeordnetenFrameLayout hinzugefügt. Das Ergebnis ist optisch dasselbe, aber die Ansichtshierarchie ist einfacher:

Optimierte Ansichtshierarchie mit dem Merge-Tag

Es ist offensichtlich, dass die Verwendung von <merge /> in diesem Fall funktioniert, da das übergeordnete Element der Inhaltsansicht einer Aktivität immer ein FrameLayout ist. Sie könnten diesen Trick nicht anwenden, wenn Ihr Layout zum Beispiel ein LinearLayout als Root-Tag verwenden würde. Das <merge /> kann jedoch in anderen Situationen nützlich sein. Zum Beispiel funktioniert es perfekt in Kombination mit dem Tag <include />. Sie können auch <merge/> verwenden, wenn Sie eine benutzerdefinierte zusammengesetzte Ansicht erstellen. Sehen wir uns an, wie wir dieses Tag verwenden können, um eine neue Ansicht namens OkCancelBar zu erstellen, die einfach zwei Schaltflächen mit anpassbaren Beschriftungen anzeigt. Sie können auch den vollständigen Quellcode dieses Beispiels herunterladen. Hier ist der XML-Code, der verwendet wird, um diese benutzerdefinierte Ansicht über einem Bild anzuzeigen:

<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>

Dieses neue Layout führt zu folgendem Ergebnis auf einem Gerät:

Erstellen einer benutzerdefinierten Ansicht mit dem Merge-Tag

Der Quellcode von OkCancelBar ist sehr einfach, da die beiden Schaltflächen in einer externen XML-Datei definiert sind, die mit einemLayoutInflate geladen wird. Wie im folgenden Ausschnitt zu sehen ist, wird das XML-Layout R.layout.okcancelbar mit OkCancelBar als übergeordnetem Element aufgeblasen:

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(); }}

Die beiden Schaltflächen sind in dem folgenden XML-Layout definiert. Wie Sie sehen können, verwenden wir den <merge />-Tag, um die beiden Schaltflächen direkt zum OkCancelBar hinzuzufügen. Jede Schaltfläche ist in derselben externen XML-Layout-Datei enthalten, damit sie leichter zu pflegen sind; wir überschreiben einfach ihre 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>

Wir haben eine flexible und leicht zu pflegende benutzerdefinierte Ansicht erstellt, die eine effiziente Ansichtshierarchie erzeugt:

Die resultierende Hierarchie ist einfach und effizient

Das <merge />-Tag ist äußerst nützlich und kann in Ihrem Code Wunder bewirken. Es leidet jedoch unter einigen Einschränkungen:

  • <merge /> kann nur als Wurzel-Tag eines XML-Layouts verwendet werden
  • Wenn Sie ein Layout aufblasen, das mit einem <merge /> beginnt, müssen Sie ein übergeordnetes ViewGroup angeben und attachToRoot auf true setzen (siehe die Dokumentation der Methode inflate(int, android.view.ViewGroup, boolean))

Leave a Reply