Qual é o objetivo da tag <merge> do Android nos layouts XML?

325

Eu li o post de Romain Guy na <merge />tag, mas ainda não entendo como é útil. É uma espécie de substituição da <Frame />tag ou é usada da seguinte maneira:

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

então <include />o código em outro arquivo?

cesar
fonte

Respostas:

586

<merge/> é útil porque pode se livrar de ViewGroups desnecessários, ou seja, layouts que são simplesmente usados ​​para agrupar outras visualizações e não servem para nada.

Por exemplo, se você <include/>usasse um layout de outro arquivo sem usar a mesclagem, os dois arquivos poderiam ter a seguinte aparência:

layout1.xml:

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

layout2.xml:

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

que é funcionalmente equivalente a este layout único:

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

Esse FrameLayout no layout2.xml pode não ser útil. <merge/>ajuda a se livrar dele. Veja como é usar a mesclagem (layout1.xml não muda):

layout2.xml:

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

Isso é funcionalmente equivalente a este layout:

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

mas como você está usando, <include/>pode reutilizar o layout em outro lugar. Ele não precisa ser usado para substituir apenas os FrameLayouts - você pode usá-lo para substituir qualquer layout que não esteja adicionando algo útil à aparência / comportamento da sua exibição.

blazeroni
fonte
17
Neste exemplo, você pode apenas fazer o layout2.xml conter apenas <TextView />, nada mais.
Karu
21
É verdade que um TextView simples poderia ser usado no layout2, mas isso seria algo completamente diferente e não seria útil como exemplo na resposta a esta pergunta.
Dave
Em conjunto com a tag <include>, é sempre útil usar a tag <merge>.
Anshul
38
@Karu: você está certo, a marca de mesclagem não é necessária neste exemplo, mas é apenas porque há um elemento no layout2. Se o layout2 tiver vários elementos, DEVE ter um nó raiz para ser XML válido e é quando a marca de mesclagem será útil.
gMale
3
Então, como você especificaria se <merge> tem orientação vertical ou horizontal? E como você dá um layout_weight?
IgorGanapolsky
304

A tag de inclusão

A <include>tag permite dividir seu layout em vários arquivos: ajuda a lidar com problemas complexos interface de usuário ou longa.

Suponhamos que você divida seu layout complexo usando dois arquivos de inclusão, como a seguir:

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>

Então você precisa escrever include1.xmle include2.xml.

Lembre-se de que o xml dos arquivos de inclusão é simplesmente despejado em seu top_level_activitylayout no momento da renderização (praticamente como a #INCLUDEmacro para C).

Os arquivos de inclusão são xml simples do layout jane.

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

Vejo? Nada chique. Observe que você ainda precisa declarar o namespace do Android com xmlns:android="http://schemas.android.com/apk/res/android.

Portanto, a versão renderizada do 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>

No seu código java, tudo isso é transparente: findViewById(R.id.textView1)na sua classe de atividade retorna o widget correto (mesmo que esse widget tenha sido declarado em um arquivo xml diferente do layout da atividade).

E a cereja no topo: o editor visual lida bem com a coisa. O layout de nível superior é renderizado com o xml incluído.

O enredo engrossa

Como um arquivo de inclusão é um arquivo xml de layout clássico, significa que ele deve ter um elemento superior. Portanto, caso seu arquivo precise incluir mais de um widget, você precisará usar um layout.

Digamos que include1.xmlagora TextViewhaja dois : um layout deve ser declarado. Vamos escolher 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>

O top_level_activity.xml será renderizado como:

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

Mas espere que os dois níveis LinearLayoutsejam redundantes !

De fato, os dois aninhados LinearLayoutnão servem para nada, pois os dois TextViewpodem ser incluídos exatamentelayout1 para a mesma renderização .

Então o que nós podemos fazer?

Digite a tag de mesclagem

A <merge>tag é apenas uma tag fictícia que fornece um elemento de nível superior para lidar com esse tipo de problemas de redundância.

Agora include1.xml se torna:

<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 agora top_level_activity.xml é renderizado como:

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

Você salvou um nível de hierarquia, evita uma visão inútil: o Romain Guy já dorme melhor.

Você não está mais feliz agora?

O nome é carl
fonte
23
Excelente descrição.
22614 RichfieldHH
4
explica muito claramente, deve ser escolhido como resposta
lalitm
2
Excelente, sem dúvida essa deve ser a resposta aceita.
Gaurav Jain
1
não entendi nada .. e se o LinearLayout externo for vertical, por exemplo, mas as duas visualizações de texto no include1.xml deveriam ser horizontais? a mesclagem nesse caso não salva o layout que eu queria. O que pode ser feito sobre isso?
Yonatan Nir
A fusão @YonatanNir não é o que você precisa no seu caso, claramente. se você realmente precisa nivelar a hierarquia de visualizações, talvez seja possível usar RelativeLayoutou desenhar as visualizações manualmente #
21415 Abhijit
19

O blazeroni já deixou bem claro, só quero acrescentar alguns pontos.

  • <merge> é usado para otimizar layouts.É usado para reduzir o aninhamento desnecessário.
  • quando um layout que contém uma <merge>tag é adicionado a outro layout, o <merge>nó é removido e sua visualização filho é adicionada diretamente ao novo pai.
Anshul
fonte
10

Para ter um conhecimento mais profundo do que está acontecendo, criei o exemplo a seguir. Dê uma olhada nos arquivos activity_main.xml e content_profile.xml .

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>

Aqui, o arquivo de layout inteiro quando inflado se parece com isso.

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

Veja se há um LinearLayout dentro do LinearLayout pai, que não serve a nenhum propósito e é redundante. Uma olhada no layout através da ferramenta Layout Inspector explica claramente isso.

insira a descrição da imagem aqui

content_profile.xml após atualizar o código para usar a mesclagem em vez de um ViewGroup como 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>

Agora, nosso layout fica assim

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

Aqui vemos que o LinearLayout ViewGroup redundante é removido. Agora, a ferramenta Inspetor de layout fornece a seguinte hierarquia de layout.

insira a descrição da imagem aqui

Portanto, sempre tente usar a mesclagem quando o layout pai puder posicionar os layouts filhos, ou mais precisamente use a mesclagem quando entender que haverá um grupo de visualizações redundantes na hierarquia.

capt.swag
fonte
5

Outro motivo para usar a mesclagem é ao usar grupos de exibição personalizados em ListViews ou GridViews. Em vez de usar o padrão viewHolder em um adaptador de lista, você pode usar uma exibição customizada. A visualização personalizada aumentaria um xml cuja raiz é uma tag de mesclagem. Código para adaptador:

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

aqui está o grupo de visualização personalizado:

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

e aqui está o 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
fonte
Você está sugerindo que, se você usasse um RelativeLayout em seu arquivo XML e seu ViewGroup personalizado herdado de RelativeLayout, haveria dois RelativeLayouts, um aninhado no outro?
Scott Biggs