Android布局中的<merge>标签的目的是什么?

ces*_*sar 299 code-reuse android include android-layout

我在标签上看过Romain Guy的帖子<merge />,但我仍然不明白它是如何有用的.它是<Frame />标签的一种替代品,还是像这样使用:

<merge xmlns:android="....">
<LinearLayout ...>
    .
    .
    .
</LinearLayout>
</merge>
Run Code Online (Sandbox Code Playgroud)

那么<include />代码在另一个文件中?

bla*_*oni 553

<merge/> 是有用的,因为它可以摆脱不需要的ViewGroups,即简单地用于包装其他视图并且本身没有用途的布局.

例如,如果您<include/>使用其他文件进行布局而不使用合并,则这两个文件可能如下所示:

layout1.xml:

<FrameLayout>
   <include layout="@layout/layout2"/>
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

layout2.xml:

<FrameLayout>
   <TextView />
   <TextView />
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

这在功能上等同于这个单一布局:

<FrameLayout>
   <FrameLayout>
      <TextView />
      <TextView />
   </FrameLayout>
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

layout2.xml中的FrameLayout可能没用. <merge/>有助于摆脱它.这是使用merge看起来的样子(layout1.xml不会改变):

layout2.xml:

<merge>
   <TextView />
   <TextView />
</merge>
Run Code Online (Sandbox Code Playgroud)

这在功能上等同于此布局:

<FrameLayout>
   <TextView />
   <TextView />
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

但是既然你正在使用,<include/>你可以在其他地方重用布局.它不必用于仅替换FrameLayouts - 您可以使用它来替换任何未添加对视图外观/行为有用的内容的布局.

  • @Karu:你说得对,在这个例子中不需要合并标签,但这只是因为 layout2 中有一个元素。如果 layout2 有多个元素,那么它必须有一个根节点才能成为有效的 XML,这时合并标签就派上用场了。 (32认同)
  • 没错,可以在layout2中使用一个简单的TextView,但是那将是一个完全不同的东西,并且在这个问题的答案中没有用作示例. (19认同)
  • 在这个例子中,你可以让layout2.xml只包含`<TextView />`,没有别的. (16认同)
  • 那么如何指定<merge>是垂直方向还是水平方向?你如何给出layout_weight? (3认同)

Nam*_*arl 284

包含标签

<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>
Run Code Online (Sandbox Code Playgroud)

然后你需要写include1.xmlinclude2.xml.

请记住,包含文件中的xml 在渲染时被简单地转储top_level_activity布局中(非常类似于#INCLUDEC 的宏).

include文件是plain 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"/>
Run Code Online (Sandbox Code Playgroud)

...和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" />
Run Code Online (Sandbox Code Playgroud)

看到?没有什么花哨.请注意,您仍然需要声明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>
Run Code Online (Sandbox Code Playgroud)

在您的java代码中,所有这些都是透明的:findViewById(R.id.textView1)在您的activity类中返回正确的小部件(即使该小部件是在与活动布局不同的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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

但等待两个级别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>
Run Code Online (Sandbox Code Playgroud)

现在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>
Run Code Online (Sandbox Code Playgroud)

您保存了一个层次结构级别,避免一个无用的视图:Romain Guy已经睡得更好了.

你现在不开心吗?

  • 优秀的描述. (21认同)
  • 解释得很清楚,应该选择作为答案 (4认同)
  • 非常好,毫无疑问这应该是公认的答案. (2认同)
  • 不明白什么.. 如果外部 LinearLayout 是垂直的,但 include1.xml 中的 2 个文本视图应该是水平的怎么办?这种情况下的合并不会保存我想要的布局。可以做些什么呢? (2认同)

Ans*_*hul 16

blazeroni已经说得很清楚了,我只想补充几点.

  • <merge> 用于优化布局.它用于减少不必要的嵌套.
  • 当包含<merge>标记的布局添加到另一个布局中时,该<merge>节点将被删除,其子视图将直接添加到新父级.
  • <merge>tag特别有用,<merge>用于插入其他布局的内容.


cap*_*wag 10

为了更深入地了解正在发生的事情,我创建了以下示例.查看activity_main.xmlcontent_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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

在这里,充气时整个布局文件看起来像这样.

<LinearLayout>
    <LinearLayout>
        <TextView />
        <TextView />
    </LinearLayout>
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

看到父LinearLayout中有一个LinearLayout,它不用于任何目的并且是多余的.通过Layout Inspector工具查看布局清楚地解释了这一点.

在此输入图像描述

更新代码以使用merge而不是像LinearLayout这样的ViewGroup之后的content_profile.xml.

<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>
Run Code Online (Sandbox Code Playgroud)

现在我们的布局看起来像这样

<LinearLayout>
    <TextView />
    <TextView />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

在这里,我们看到删除了冗余的LinearLayout ViewGroup.现在,布局检查器工具提供以下布局层次结构.

在此输入图像描述

因此,当您的父布局可以定位子布局时,请始终尝试使用合并,或者在您了解层次结构中将存在冗余视图组时更准确地使用合并.


小智 5

使用合并的另一个原因是在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是自定义视图组:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这是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>
Run Code Online (Sandbox Code Playgroud)