Какова цель тега ‹merge› Android в макетах XML?

Я прочитал сообщение Ромена Гая о теге <merge />, но До сих пор не понимаю, чем это полезно. Это своего рода замена тега <Frame /> или он используется так:

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

затем <include /> код в другом файле?


person cesar    schedule 12.01.2012    source источник


Ответы (5)


<merge/> полезен, потому что он может избавиться от ненужных групп просмотра, то есть макетов, которые просто используются для обертывания других представлений и сами по себе не служат никакой цели.

Например, если вы должны <include/> создать макет из другого файла без использования слияния, два файла могут выглядеть примерно так:

layout1.xml:

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

layout2.xml:

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

что функционально эквивалентно этому единственному макету:

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

Этот FrameLayout в layout2.xml может оказаться бесполезным. <merge/> помогает избавиться от этого. Вот как это выглядит при использовании слияния (layout1.xml не меняется):

layout2.xml:

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

Функционально это эквивалентно этому макету:

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

но поскольку вы используете <include/>, вы можете повторно использовать макет в другом месте. Его не нужно использовать для замены только FrameLayouts - вы можете использовать его для замены любого макета, который не добавляет что-то полезное к тому, как выглядит / ведет себя ваше представление.

person blazeroni    schedule 19.06.2012
comment
В этом примере вы можете просто сделать layout2.xml содержащим только <TextView /> и ничего больше. - person Karu; 12.09.2013
comment
Правда, вместо этого в layout2 можно было бы использовать простой TextView, однако тогда это было бы совсем другое дело и бесполезно в качестве примера для ответа на этот вопрос. - person Dave; 03.10.2013
comment
В сочетании с тегом ‹include› всегда полезно использовать тег ‹merge›. - person Anshul; 10.12.2013
comment
@Karu: вы правы, тег слияния в этом примере не нужен, но это только потому, что в layout2 есть один элемент. Если layout2 имеет несколько элементов, тогда он ДОЛЖЕН иметь корневой узел, чтобы быть допустимым XML, и тогда тег слияния пригодится. - person gMale; 22.02.2014
comment
Итак, как бы вы указали, имеет ли ‹merge› вертикальную ориентацию или горизонтальную? А как вы ставите layout_weight? - person IgorGanapolsky; 10.06.2014
comment
@IgorGanapolsky В этом нет никакого смысла. Если вам нужны функции макета, вы, очевидно, не используете ‹merge›. - person The incredible Jan; 01.03.2021

Тег include

Тег <include> позволяет разделить макет на несколько файлов: он помогает справиться с < em> сложный или слишком длинный пользовательский интерфейс.

Предположим, вы разделили сложный макет с помощью двух включаемых файлов следующим образом:

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>

Затем нужно написать include1.xml и include2.xml.

Имейте в виду, что xml из включаемых файлов просто выгружается в ваш top_level_activity макет во время рендеринга (очень похоже на макрос #INCLUDE для C).

Включаемые файлы представляют собой простой XML-макет 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"/>

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

Видеть? Ничего фантастического. Обратите внимание, что вам все равно нужно объявить пространство имен android с помощью xmlns:android="http://schemas.android.com/apk/res/android.

Итак, обработанная версия 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>

В вашем Java-коде все это прозрачно: findViewById(R.id.textView1) в вашем классе активности возвращает правильный виджет (даже если этот виджет был объявлен в XML-файле, отличном от макета активности).

И вишенка на вершине: визуальный редактор отлично справляется с этой задачей. Макет верхнего уровня отображается с включенным xml.

Сюжет сгущается

Поскольку включаемый файл является XML-файлом классического макета, это означает, что он должен иметь один верхний элемент. Поэтому, если ваш файл должен включать более одного виджета, вам придется использовать макет.

Допустим, у include1.xml теперь два TextView: нужно объявить макет. Выберем 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>

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

Но подождите, два уровня LinearLayout избыточны!

В самом деле, два вложенных LinearLayout не служат никакой цели, поскольку два TextView могут быть включены в layout1 для точно такого же рендеринга.

Так что мы можем сделать?

Введите тег слияния

Тег <merge> - это просто фиктивный тег, который предоставляет элемент верхнего уровня для решения таких проблем с избыточностью.

Теперь include1.xml становится следующим:

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

и теперь 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="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>

Вы сохранили один уровень иерархии, избегайте одного бесполезного представления: Ромен Гай уже спит лучше.

Разве ты не стал счастливее?

person Name is carl    schedule 01.01.2014
comment
Отличное описание. - person RichieHH; 16.03.2014
comment
объясняет очень ясно, следует выбрать в качестве ответа - person lalitm; 21.05.2014
comment
Отлично, без сомнения, это должен быть принятый ответ. - person gaurav jain; 17.03.2015
comment
что-то не понял ... что, если внешний LinearLayout, например, вертикальный, но 2 текстовых представления в include1.xml должны были быть горизонтальными? слияние в этом случае не сохраняет их макет, который я хотел. Что с этим делать? - person Yonatan Nir; 08.07.2015
comment
Очевидно, что слияние @YonatanNir - это не то, что вам нужно в вашем случае. если вам действительно нужно сгладить иерархию представлений, то, возможно, вы можете использовать RelativeLayout или нарисовать представления вручную - person Abhijit; 21.07.2015
comment
Ответ blazeroni был очень хорош, но он на один шаг впереди из-за его двух примеров TextViews. Спасибо за блестящее объяснение. - person Bugs Happen; 17.08.2015
comment
идеально, подробно объяснено - person Khurram Shehzad; 22.05.2016

blazeroni уже прояснил это, я просто хочу добавить несколько моментов.

  • <merge> используется для оптимизации макетов. Он используется для уменьшения ненужной вложенности.
  • когда макет, содержащий тег <merge>, добавляется в другой макет, узел <merge> удаляется, а его дочернее представление добавляется непосредственно к новому родительскому элементу.
person Anshul    schedule 10.12.2013

Чтобы получить более подробные сведения о том, что происходит, я создал следующий пример. Взгляните на файлы activity_main.xml и 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>

Здесь весь файл макета в раздуваемом состоянии выглядит так.

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

Обратите внимание, что внутри родительского LinearLayout есть LinearLayout, который не служит никакой цели и является избыточным. Это ясно объясняет взгляд на макет с помощью инструмента Layout Inspector.

введите описание изображения здесь

content_profile.xml после обновления кода для использования слияния вместо ViewGroup, например 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>

Теперь наш макет выглядит так

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

Здесь мы видим, что избыточная группа просмотра LinearLayout удалена. Теперь инструмент Layout Inspector дает следующую иерархию макетов.

введите описание изображения здесь

Поэтому всегда старайтесь использовать слияние, когда родительский макет может позиционировать дочерние макеты, или, точнее, используйте слияние, когда вы понимаете, что будет избыточное представление. группа в иерархии.

person capt.swag    schedule 11.10.2017

Еще одна причина для использования слияния - использование настраиваемых групп просмотра в ListViews или GridViews. Вместо использования шаблона viewHolder в адаптере списка вы можете использовать настраиваемое представление. Пользовательское представление расширит XML-файл, корнем которого является тег слияния. Код для адаптера:

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

вот настраиваемая группа просмотра:

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

и вот 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>
person mmienko    schedule 08.02.2015
comment
Вы подразумеваете, что если бы вы использовали RelativeLayout в своем XML-файле и свою настраиваемую ViewGroup, унаследованную от RelativeLayout, тогда было бы два RelativeLayout, одно вложенное в другое? - person SMBiggs; 02.08.2019