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

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

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

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

7 ответов

Решение

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

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

layout1.xml:

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

layout2.xml:

<FrameLayout>
   <TextView />
</FrameLayout>

который функционально эквивалентен этой единственной компоновке:

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

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

layout2.xml:

<merge>
   <TextView />
</merge>

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

<FrameLayout>
   <TextView />
</FrameLayout>

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

Тег включения

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

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

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 макрос для с).

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

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

Теперь 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>

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

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

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

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

Чтобы иметь более глубокие знания о том, что происходит, я создал следующий пример. Посмотрите на файлы 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, который не служит какой-либо цели и является избыточным. Взгляд на макет с помощью инструмента "Инспектор макетов" ясно объясняет это.

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 ViewGroup удалена. Теперь инструмент Layout Inspector предоставляет следующую иерархию макетов.

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

В простых и точных словах,

Когда макет, содержащий тег слияния, добавляется в другой макет, узел слияния удаляется, а его дочерние представления добавляются непосредственно к новому родительскому элементу.

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

пример

<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="@+id/my_id1"
layout="@layout/layout1"/>
<include android:id="@+id/my_id2"
layout="@layout/layout2"/>
</LinearLayout>

Другой причиной использования слияния является использование пользовательских групп представлений в 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>

Основываясь на официальной документации Android, вам нужно использовать только merge на месте FrameLayout если он не дает padding Или margin и т.п.

Цитировать:

Корневой фрейм слияния - если FrameLayout является корнем макета и не предоставляет фон или отступы и т. Д., Его можно заменить тегом слияния, который немного более эффективен.

Ссылка на документы

Другие вопросы по тегам