Was ist der Zweck des <merge> -Tags von Android in XML-Layouts?

325

Ich habe Romain Guys Beitrag auf dem <merge />Tag gelesen , aber ich verstehe immer noch nicht, wie nützlich er ist. Ist es eine Art Ersatz für das <Frame />Tag oder wird es so verwendet:

<merge xmlns:android="....">
<LinearLayout ...>
    .
    .
    .
</LinearLayout>
</merge>

dann <include />der Code in einer anderen Datei?

Cesar
quelle

Antworten:

584

<merge/> Dies ist nützlich, da nicht benötigte ViewGroups entfernt werden können, dh Layouts, die einfach zum Umschließen anderer Ansichten verwendet werden und selbst keinen Zweck erfüllen.

Wenn Sie beispielsweise <include/>ein Layout aus einer anderen Datei ohne Zusammenführung verwenden, sehen die beiden Dateien möglicherweise folgendermaßen aus:

layout1.xml:

<FrameLayout>
   <include layout="@layout/layout2"/>
</FrameLayout>

layout2.xml:

<FrameLayout>
   <TextView />
   <TextView />
</FrameLayout>

was funktional diesem einzelnen Layout entspricht:

<FrameLayout>
   <FrameLayout>
      <TextView />
      <TextView />
   </FrameLayout>
</FrameLayout>

Das FrameLayout in layout2.xml ist möglicherweise nicht nützlich. <merge/>hilft, es loszuwerden. So sieht es mit Merge aus (layout1.xml ändert sich nicht):

layout2.xml:

<merge>
   <TextView />
   <TextView />
</merge>

Dies entspricht funktional diesem Layout:

<FrameLayout>
   <TextView />
   <TextView />
</FrameLayout>

Da Sie jedoch verwenden <include/>, können Sie das Layout an anderer Stelle wiederverwenden. Es muss nicht verwendet werden, um nur FrameLayouts zu ersetzen. Sie können es verwenden, um jedes Layout zu ersetzen, das der Darstellung / dem Verhalten Ihrer Ansicht nichts Nützliches hinzufügt.

Blazeroni
quelle
17
In diesem Beispiel könnten Sie einfach dafür sorgen, dass layout2.xml nur <TextView />nichts anderes enthält.
Karu
21
Zwar könnte stattdessen in layout2 eine einfache Textansicht verwendet werden, dies wäre jedoch eine völlig andere Sache und als Beispiel für die Beantwortung dieser Frage nicht nützlich.
Dave
In Verbindung mit dem <include> -Tag ist es immer nützlich, das <merge> -Tag zu verwenden.
Anshul
38
@Karu: Sie haben Recht, das Zusammenführungs-Tag ist in diesem Beispiel nicht erforderlich, aber das liegt nur daran, dass layout2 ein Element enthält. Wenn layout2 mehrere Elemente hatte, MUSS es einen Stammknoten haben, um gültiges XML zu sein, und dann ist das Merge-Tag nützlich.
gMale
3
Wie würden Sie also angeben, ob <merge> vertikal oder horizontal ausgerichtet ist? Und wie gibt man ein layout_weight?
IgorGanapolsky
304

Das Include-Tag

Mit dem <include>Tag können Sie Ihr Layout in mehrere Dateien aufteilen: Es hilft beim Umgang mit komplexen oder überlangen Benutzeroberflächen.

Angenommen, Sie teilen Ihr komplexes Layout mithilfe von zwei Include-Dateien wie folgt auf:

top_level_activity.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout1" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- First include file -->
    <include layout="@layout/include1.xml" />

    <!-- Second include file -->
    <include layout="@layout/include2.xml" />

</LinearLayout>

Dann musst du schreiben include1.xmlund include2.xml.

Beachten Sie, dass die XML - Daten aus den Dateien enthalten ist einfach abgeladen in Ihrem top_level_activityLayout an die Rendering - Zeit (ziemlich ähnlich wie das #INCLUDEMakro für C).

Die Include-Dateien sind einfache Jane Layout XML.

include1.xml :

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView1"
    android:text="First include"
    android:textAppearance="?android:attr/textAppearanceMedium"/>

... und include2.xml :

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/button1"
    android:text="Button" />

Sehen? Nichts Besonderes. Beachten Sie, dass Sie den Android-Namespace noch mit deklarieren müssen xmlns:android="http://schemas.android.com/apk/res/android.

Die gerenderte Version von top_level_activity.xml lautet also:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout1" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- First include file -->
    <TextView
        android:id="@+id/textView1"
        android:text="First include"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <!-- Second include file -->
    <Button
        android:id="@+id/button1"
        android:text="Button" />


</LinearLayout>

In Ihrem Java-Code ist dies alles transparent: findViewById(R.id.textView1)In Ihrer Aktivitätsklasse wird das richtige Widget zurückgegeben (auch wenn dieses Widget in einer XML-Datei deklariert wurde, die sich vom Aktivitätslayout unterscheidet).

Und die Kirsche an der Spitze: Der visuelle Editor handhabt das Ding schwimmend. Das Layout der obersten Ebene wird mit der enthaltenen XML gerendert .

Die Handlung verdickt sich

Da eine Include-Datei eine klassische Layout-XML-Datei ist, bedeutet dies, dass sie ein oberstes Element enthalten muss. Wenn Ihre Datei also mehr als ein Widget enthalten muss, müssen Sie ein Layout verwenden.

Nehmen wir an, das include1.xmlhat jetzt zwei TextView: Ein Layout muss deklariert werden. Lassen Sie uns eine wählen LinearLayout.

include1.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout2" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:text="Second include"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <TextView
        android:id="@+id/textView2"
        android:text="More text"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

</LinearLayout>

Die Datei top_level_activity.xml wird wie folgt gerendert:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout1" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- First include file -->
    <LinearLayout 
        android:id="@+id/layout2" 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

       <TextView
            android:id="@+id/textView1"
            android:text="Second include"
            android:textAppearance="?android:attr/textAppearanceMedium"/>

       <TextView
            android:id="@+id/textView2"
            android:text="More text"
            android:textAppearance="?android:attr/textAppearanceMedium"/>

   </LinearLayout>

     <!-- Second include file -->
   <Button
        android:id="@+id/button1"
        android:text="Button" />

</LinearLayout>

Aber warten Sie, die beiden Ebenen LinearLayoutsind überflüssig !

In der Tat haben die beiden verschachtelten LinearLayoutZwecke keinen Zweck, da die beiden für genau dasselbe RenderingTextView unter aufgenommen werden könnten .layout1

Also was können wir tun?

Geben Sie das Zusammenführungs-Tag ein

Das <merge>Tag ist nur ein Dummy-Tag, das ein Element der obersten Ebene bietet, um diese Art von Redundanzproblemen zu lösen.

Jetzt wird include1.xml zu:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/textView1"
        android:text="Second include"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <TextView
        android:id="@+id/textView2"
        android:text="More text"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

</merge>

und jetzt wird top_level_activity.xml wie folgt gerendert:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout1" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- First include file --> 
    <TextView
        android:id="@+id/textView1"
        android:text="Second include"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <TextView
        android:id="@+id/textView2"
        android:text="More text"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <!-- Second include file -->
    <Button
        android:id="@+id/button1"
        android:text="Button" />

</LinearLayout>

Sie haben eine Hierarchieebene gespeichert, vermeiden Sie eine nutzlose Ansicht: Romain Guy schläft bereits besser.

Bist du jetzt nicht glücklicher?

Name ist Carl
quelle
23
Hervorragende Beschreibung.
RichieHH
4
erklärt sehr deutlich, sollte als Antwort gewählt werden
lalitm
2
Ausgezeichnet, ohne Zweifel sollte dies die akzeptierte Antwort sein.
Gaurav Jain
1
habe etwas nicht verstanden .. was ist, wenn das äußere LinearLayout zum Beispiel vertikal ist, aber die 2 Textansichten in der include1.xml horizontal sein sollten? Die Zusammenführung speichert in diesem Fall nicht das Layout, das ich wollte. Was kann man dagegen tun?
Yonatan Nir
@YonatanNir Merge ist eindeutig nicht das, was Sie in Ihrem Fall brauchen. Wenn Sie die Ansichtshierarchie wirklich reduzieren müssen, können Sie RelativeLayoutdie Ansichten möglicherweise manuell verwenden oder zeichnen
Abhijit
19

blazeroni hat es schon ziemlich deutlich gemacht, ich möchte nur ein paar punkte hinzufügen.

  • <merge> wird zur Optimierung von Layouts verwendet. Es wird zur Reduzierung unnötiger Verschachtelungen verwendet.
  • Wenn ein Layout mit einem <merge>Tag zu einem anderen Layout hinzugefügt wird, wird der <merge>Knoten entfernt und seine untergeordnete Ansicht direkt dem neuen übergeordneten Element hinzugefügt.
Anshul
quelle
10

Um genauer zu wissen, was passiert, habe ich das folgende Beispiel erstellt. Schauen Sie sich die Dateien activity_main.xml und content_profile.xml an .

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

</LinearLayout>

content_profile.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Howdy" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hi there" />

</LinearLayout>

Hier sieht die gesamte Layoutdatei im aufgeblasenen Zustand so aus.

<LinearLayout>
    <LinearLayout>
        <TextView />
        <TextView />
    </LinearLayout>
</LinearLayout>

Stellen Sie sicher, dass sich im übergeordneten LinearLayout ein LinearLayout befindet, das keinen Zweck erfüllt und redundant ist. Ein Blick auf das Layout mit dem Layout Inspector-Tool erklärt dies deutlich.

Geben Sie hier die Bildbeschreibung ein

content_profile.xml nach dem Aktualisieren des Codes, um Merge anstelle einer ViewGroup wie LinearLayout zu verwenden.

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Howdy" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hi there" />

</merge>

Jetzt sieht unser Layout so aus

<LinearLayout>
    <TextView />
    <TextView />
</LinearLayout>

Hier sehen wir, dass die redundante LinearLayout ViewGroup entfernt wird. Das Layout Inspector-Tool bietet jetzt die folgende Layouthierarchie.

Geben Sie hier die Bildbeschreibung ein

Versuchen Sie daher immer, die Zusammenführung zu verwenden, wenn Ihr übergeordnetes Layout Ihre untergeordneten Layouts positionieren kann, oder verwenden Sie die Zusammenführung genauer, wenn Sie verstehen, dass es in der Hierarchie eine redundante Ansichtsgruppe geben wird.

capt.swag
quelle
5

Ein weiterer Grund für die Verwendung der Zusammenführung ist die Verwendung benutzerdefinierter Ansichtsgruppen in ListViews oder GridViews. Anstatt das viewHolder-Muster in einem Listenadapter zu verwenden, können Sie eine benutzerdefinierte Ansicht verwenden. Die benutzerdefinierte Ansicht würde eine XML aufblasen, deren Stamm ein Zusammenführungs-Tag ist. Code für Adapter:

public class GridViewAdapter extends BaseAdapter {
     // ... typical Adapter class methods
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
        WallpaperView wallpaperView;
        if (convertView == null)
           wallpaperView = new WallpaperView(activity);
        else
            wallpaperView = (WallpaperView) convertView;

        wallpaperView.loadWallpaper(wallpapers.get(position), imageWidth);
        return wallpaperView;
    }
}

Hier ist die benutzerdefinierte Ansichtsgruppe:

public class WallpaperView extends RelativeLayout {

    public WallpaperView(Context context) {
        super(context);
        init(context);
    }
    // ... typical constructors

    private void init(Context context) {
        View.inflate(context, R.layout.wallpaper_item, this);
        imageLoader = AppController.getInstance().getImageLoader();
        imagePlaceHolder = (ImageView) findViewById(R.id.imgLoader2);
        thumbnail = (NetworkImageView) findViewById(R.id.thumbnail2);
        thumbnail.setScaleType(ImageView.ScaleType.CENTER_CROP);
    }

    public void loadWallpaper(Wallpaper wallpaper, int imageWidth) {
        // ...some logic that sets the views
    }
}

und hier ist das XML:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <ImageView
        android:id="@+id/imgLoader"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ico_loader" />

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/thumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</merge>
mmienko
quelle
Bedeuten Sie, dass es zwei RelativeLayouts geben würde , wenn Sie ein RelativeLayout in Ihrer XML-Datei und Ihre benutzerdefinierte ViewGroup verwenden würden, die von RelativeLayout geerbt wurde?
Scott Biggs