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

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

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

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

Ответ 1

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

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

Ответ 2

Тег include

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

Предположим, вы разделили сложный макет с помощью двух файлов 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 для C).

Входящие файлы представляют собой простой 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.

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

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

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

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>

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

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

Ответ 3

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

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

Ответ 4

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

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

тег слияния особенно полезен в сочетании с 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>

Ответ 5

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

enter image description here

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

enter image description here

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

Ответ 6

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