Qual è lo scopo del tag <merge> di Android nei layout XML?


325

Ho letto il post di Romain Guy sul <merge />tag, ma non capisco ancora quanto sia utile. È una sorta di sostituzione del <Frame />tag o è usato in questo modo:

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

quindi <include />il codice in un altro file?

Risposte:


586

<merge/> è utile perché può sbarazzarsi di ViewGroup non necessari, ovvero layout che sono semplicemente usati per avvolgere altre viste e non servono a nessuno scopo.

Ad esempio, se si fosse in <include/>un layout da un altro file senza usare l'unione, i due file potrebbero apparire in questo modo:

layout1.xml:

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

layout2.xml:

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

che è funzionalmente equivalente a questo singolo layout:

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

Quel FrameLayout in layout2.xml potrebbe non essere utile. <merge/>aiuta a liberarsene. Ecco come appare usando merge (layout1.xml non cambia):

layout2.xml:

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

Questo è funzionalmente equivalente a questo layout:

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

ma poiché stai usando <include/>puoi riutilizzare il layout altrove. Non deve essere utilizzato per sostituire solo FrameLayouts: puoi usarlo per sostituire qualsiasi layout che non aggiunge qualcosa di utile al modo in cui la tua vista appare / si comporta.


17
In questo esempio potresti semplicemente fare in modo che layout2.xml contenga solo <TextView />nient'altro.
Karu,

21
È vero, al posto di layout2 potrebbe essere usato un semplice TextView, tuttavia questa sarebbe una cosa completamente diversa e non utile come esempio nella risposta a questa domanda.
Dave,

Insieme al tag <include> è sempre utile usare il tag <merge>.
Anshul,

38
@Karu: hai ragione, il tag di unione non è necessario in questo esempio ma è solo perché c'è un elemento in layout2. Se layout2 aveva più elementi, allora DEVE avere un nodo radice per essere XML valido ed è allora che il tag di unione è utile.
gMale

3
Quindi come specificheresti se <merge> ha orientamento verticale o orizzontale? E come si dà un layout_weight?
IgorGanapolsky,

304

Il tag include

Il <include>tag consente di dividere il layout in più file: aiuta a gestire complessi l'interfaccia utente o troppo lunga.

Supponiamo di dividere il layout complesso usando due file include come segue:

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>

Quindi devi scrivere include1.xmle include2.xml.

Tieni presente che il file XML dai file include viene semplicemente scaricato nel top_level_activitylayout al momento del rendering (in modo molto simile alla #INCLUDEmacro per C).

I file include sono xml di layout jane semplici.

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

... e 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" />

Vedere? Nulla di bello. Nota che devi ancora dichiarare lo spazio dei nomi Android con xmlns:android="http://schemas.android.com/apk/res/android.

Quindi la versione renderizzata di 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 -->
    <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>

Nel tuo codice java, tutto questo è trasparente: findViewById(R.id.textView1) nella tua classe di attività restituisce il widget corretto (anche se quel widget è stato dichiarato in un file xml diverso dal layout dell'attività).

E la ciliegina sulla torta: l' editor visuale gestisce la cosa a meraviglia. Viene visualizzato il layout di livello superiore con l'xml incluso.

La trama si infittisce

Poiché un file include è un file xml di layout classico, significa che deve avere un elemento superiore. Pertanto, nel caso in cui il file debba includere più di un widget, è necessario utilizzare un layout.

Diciamo che include1.xmlora ha due TextView: un layout deve essere dichiarato. Scegliamo a 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>

Il top_level_activity.xml sarà tradotto in:

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

Ma aspetta che i due livelli di LinearLayoutsiano ridondanti !

In effetti, i due nidificati LinearLayoutnon servono a nulla in quanto i due TextViewpotrebbero essere inclusi esattamentelayout1 per lo stesso rendering .

Quindi cosa possiamo fare?

Immettere il tag di unione

Il <merge>tag è solo un tag fittizio che fornisce un elemento di livello superiore per affrontare questo tipo di problemi di ridondanza.

Ora include1.xml diventa:

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

e ora top_level_activity.xml viene visualizzato come:

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

Hai salvato un livello gerarchico, evita una visione inutile: Romain Guy dorme già meglio.

Non sei più felice adesso?


23
Descrizione eccellente.
RichieHH,

4
spiega molto chiaramente, dovrebbe essere scelta come risposta
lalitm

2
Eccellente, senza dubbio questa dovrebbe essere la risposta accettata.
gaurav jain,

1
non ho capito qualcosa ... e se il LinearLayout esterno fosse verticale ad esempio, ma le 2 visualizzazioni di testo in include1.xml dovessero essere orizzontali? l'unione in quel caso non salva il layout che volevo. Cosa si può fare al riguardo?
Yonatan Nir,

La fusione di @YonatanNir non è chiaramente ciò di cui hai bisogno nel tuo caso. se hai davvero bisogno di appiattire la gerarchia delle viste, allora forse puoi usare RelativeLayouto disegnare manualmente le viste
Abhijit

19

Blazeroni lo ha già chiarito abbastanza, voglio solo aggiungere alcuni punti.

  • <merge> viene utilizzato per ottimizzare i layout. Viene utilizzato per ridurre l'annidamento non necessario.
  • quando un layout contenente <merge>tag viene aggiunto in un altro layout, il <merge>nodo viene rimosso e la sua vista figlio viene aggiunta direttamente al nuovo genitore.

10

Per avere una conoscenza più approfondita di ciò che sta accadendo, ho creato il seguente esempio. Dai un'occhiata a activity_main.xml e content_profile.xml file .

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>

Qui, l'intero file di layout quando gonfiato appare così.

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

Vedi che c'è un LinearLayout all'interno del genitore LinearLayout che non ha alcuno scopo ed è ridondante. Uno sguardo al layout attraverso lo strumento Inspector layout lo spiega chiaramente.

inserisci qui la descrizione dell'immagine

content_profile.xml dopo aver aggiornato il codice per utilizzare merge invece di un ViewGroup come LinearLayout.

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

Ora il nostro layout è simile a questo

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

Qui vediamo che il gruppo ridondante LinearLayout ViewGroup è stato rimosso. Ora lo strumento Impostazioni Layout offre la seguente gerarchia di layout.

inserisci qui la descrizione dell'immagine

Quindi cerca sempre di usare l' unione quando il layout principale può posizionare i layout secondari o, più precisamente, usa l' unione quando capisci che ci sarà un gruppo di vista ridondante nella gerarchia.


5

Un altro motivo per utilizzare l'unione è quando si utilizzano viewgroup personalizzati in ListViews o GridViews. Invece di utilizzare il modello viewHolder in un adattatore elenco, è possibile utilizzare una vista personalizzata. La vista personalizzata gonfia un xml la cui radice è un tag di unione. Codice per adattatore:

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

ecco il viewgroup personalizzato:

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

ed ecco l'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>

Stai implicando che se avessi usato un RelativeLayout nel tuo file XML e il tuo ViewGroup personalizzato ereditato da RelativeLayout, allora ci sarebbero due RelativeLayout, uno nidificato nell'altro?
Scott Biggs,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.