Do czego służy tag <merge> Androida w układach XML?

325

Przeczytałem post Romaina Guy'a na <merge />tagu, ale nadal nie rozumiem, jak jest przydatny. Czy jest to swego rodzaju zamiana <Frame />tagu, czy też jest on używany w ten sposób:

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

to <include />kod w innym pliku?

cesar
źródło

Odpowiedzi:

584

<merge/> jest przydatny, ponieważ może pozbyć się niepotrzebnych grup ViewGroup, tj. układów, które są po prostu używane do zawijania innych widoków i same w sobie nie służą.

Na przykład, jeśli przejdziesz do <include/>układu z innego pliku bez użycia scalania, dwa pliki mogą wyglądać mniej więcej tak:

layout1.xml:

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

layout2.xml:

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

co jest funkcjonalnie równoważne z tym pojedynczym układem:

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

FrameLayout w layout2.xml może nie być przydatny. <merge/>pomaga się go pozbyć. Oto jak to wygląda przy użyciu scalania (layout1.xml nie zmienia się):

layout2.xml:

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

Jest to funkcjonalnie równoważne z tym układem:

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

ale ponieważ używasz <include/>, możesz ponownie użyć układu w innym miejscu. Nie trzeba go zastępować tylko FrameLayouts - możesz go użyć do zastąpienia dowolnego układu, który nie dodaje niczego użytecznego do wyglądu / zachowania twojego widoku.

blazeroni
źródło
17
W tym przykładzie możesz po prostu sprawić, że layout2.xml zawiera tylko <TextView />, nic więcej.
Karu
21
To prawda, że ​​w układzie layout2 można użyć prostego TextView, jednak byłaby to zupełnie inna rzecz i nie byłaby przydatna jako przykład w odpowiedzi na to pytanie.
Dave
W połączeniu ze znacznikiem <include> zawsze przydatne jest użycie znacznika <merge>.
Anshul,
38
@Karu: masz rację, tag korespondencji seryjnej nie jest konieczny w tym przykładzie, ale tylko dlatego, że jest jeden element w layout2. Jeśli layout2 miał wiele elementów, MUSI mieć węzeł główny, aby być poprawnym plikiem XML i wtedy przydatny jest tag scalania.
gMale
3
Jak więc określić, czy <merge> ma orientację pionową czy poziomą? A jak podajesz wagę układu?
IgorGanapolsky
304

Tag dołączania

<include>Tag pozwala dzielić swój układ na wiele plików: pomaga do czynienia ze skomplikowanym lub zbyt długie interfejsu użytkownika.

Załóżmy, że podzieliłeś swój złożony układ za pomocą dwóch plików dołączanych w następujący sposób:

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>

Następnie musisz napisać include1.xmli include2.xml.

Należy pamiętać, że plik XML z plików dołączanych jest po prostu zrzucany w top_level_activityukładzie podczas renderowania (podobnie jak #INCLUDEmakro dla C).

Pliki dołączane to zwykły układ jane 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"/>

... i dołącz plik 2.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" />

Widzieć? Nic fajnego. Pamiętaj, że nadal musisz zadeklarować przestrzeń nazw Androida xmlns:android="http://schemas.android.com/apk/res/android.

Więc wygenerowana wersja top_level_activity.xml jest:

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

W kodzie java wszystko to jest przezroczyste: findViewById(R.id.textView1)w twojej klasie aktywności zwraca poprawny widget (nawet jeśli ten widget został zadeklarowany w pliku xml innym niż układ aktywności).

A wisienka na górze: edytor wizualny radzi sobie z tym płynnie. Układ najwyższego poziomu jest renderowany z dołączonym plikiem XML.

Fabuła gęstnieje

Ponieważ plik dołączany jest plikiem XML w klasycznym układzie, oznacza to, że musi mieć jeden górny element. Jeśli więc plik musi zawierać więcej niż jeden widżet, musisz użyć układu.

Powiedzmy, że include1.xmlteraz są dwa TextView: należy zadeklarować układ. Wybierzmy 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>

Plik top_level_activity.xml będzie renderowany jako:

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

Ale poczekaj dwa poziomy LinearLayoutsą zbędne !

Rzeczywiście, dwa zagnieżdżone LinearLayoutnie służą żadnemu celowi, ponieważ dwa TextViewmogą być uwzględnione layout1dla dokładnie tego samego renderowania .

Więc co możemy zrobić?

Wpisz tag scalania

<merge>Tag tag tylko manekin, który stanowi element najwyższego poziomu do czynienia z tego rodzaju zagadnień związanych ze zwolnieniem.

Teraz include1.xml staje się:

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

a teraz top_level_activity.xml jest renderowany jako:

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

Zapisałeś jeden poziom hierarchii, unikaj jednego niepotrzebnego widoku: Romain Guy już lepiej śpi.

Nie jesteś teraz szczęśliwszy?

Imię to Carl
źródło
23
Doskonały opis
RichieHH
4
wyjaśnia bardzo jasno, należy wybrać jako odpowiedź
lalitm
2
Doskonale, bez wątpienia powinna to być zaakceptowana odpowiedź.
gaurav jain
1
nie rozumiem czegoś ... co jeśli zewnętrzny LinearLayout jest na przykład pionowy, ale 2 widoki tekstu w pliku include1.xml miały być poziome? scalenie w tym przypadku nie zapisuje ich układu, którego chciałem. Co można z tym zrobić?
Yonatan Nir,
Oczywiście łączenie @YonatanNir nie jest tym, czego potrzebujesz w twoim przypadku. jeśli naprawdę potrzebujesz spłaszczyć hierarchię widoków, być może możesz użyć RelativeLayoutlub narysować widoki ręcznie
Abhijit
19

blazeroni już to wyjaśnił, chcę tylko dodać kilka punktów.

  • <merge> służy do optymalizacji układów i służy do ograniczania niepotrzebnego zagnieżdżania.
  • po <merge>dodaniu układu zawierającego znacznik do innego układu <merge>węzeł jest usuwany, a jego widok potomny jest dodawany bezpośrednio do nowego elementu nadrzędnego.
Anszul
źródło
10

Aby uzyskać bardziej dogłębną wiedzę o tym, co się dzieje, stworzyłem następujący przykład. Zobacz pliki activity_main.xml i 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>

Tutaj cały plik układu po napompowaniu wygląda tak.

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

Sprawdź, czy w nadrzędnym LinearLayout znajduje się LinearLayout, który nie służy żadnemu celowi i jest zbędny. Spojrzenie na układ za pomocą narzędzia Layout Inspector wyraźnie to wyjaśnia.

wprowadź opis zdjęcia tutaj

content_profile.xml po zaktualizowaniu kodu w celu użycia scalania zamiast ViewGroup, takiego jak 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>

Teraz nasz układ wygląda tak

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

Widzimy tutaj, że nadmiarowa grupa LinearLayout ViewGroup została usunięta. Teraz narzędzie Inspektor układu daje następującą hierarchię układu.

wprowadź opis zdjęcia tutaj

Dlatego zawsze staraj się używać scalania, gdy układ nadrzędny może pozycjonować układy podrzędne, a dokładniej użyj scalania, gdy zrozumiesz, że w hierarchii będzie nadmiarowa grupa widoków.

capt.swag
źródło
5

Innym powodem użycia scalania jest użycie niestandardowych grup widoków w ListViews lub GridViews. Zamiast używać wzorca viewHolder w adapterze listy, można użyć widoku niestandardowego. Widok niestandardowy nadmuchałby plik XML, którego katalog główny jest znacznikiem scalającym. Kod adaptera:

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

oto niestandardowa grupa widokowa:

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

a oto 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
źródło
Czy sugerujesz, że jeśli użyjesz RelativeLayout w pliku XML, a Twoja niestandardowa grupa ViewGroup odziedziczy po RelativeLayout, to będą dwie RelativeLayout, jedna zagnieżdżona w drugiej?
Scott Biggs