【问题标题】:What is the purpose of Android's <merge> tag in XML layouts?Android 的 <merge> 标签在 XML 布局中的用途是什么?
【发布时间】:2012-02-08 17:33:08
【问题描述】:

我已经阅读了&lt;merge /&gt; 标签上的Romain Guy's post,但我仍然不明白它有什么用处。它是&lt;Frame /&gt; 标签的某种替代品,还是像这样使用:

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

然后&lt;include /&gt;另一个文件中的代码?

【问题讨论】:

    标签: android android-layout include code-reuse


    【解决方案1】:

    &lt;merge/&gt; 很有用,因为它可以摆脱不需要的 ViewGroup,即仅用于包装其他视图且本身无用的布局。

    例如,如果您要在不使用合并的情况下从另一个文件 &lt;include/&gt; 布局,则这两个文件可能如下所示:

    layout1.xml:

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

    layout2.xml:

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

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

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

    layout2.xml 中的 FrameLayout 可能没用。 &lt;merge/&gt; 有助于摆脱它。这是使用合并的样子(layout1.xml 不会改变):

    layout2.xml:

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

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

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

    但由于您使用的是&lt;include/&gt;,您可以在其他地方重用布局。它不必仅用于替换 FrameLayouts - 您可以使用它来替换任何不会为您的视图外观/行为方式添加有用内容的布局。

    【讨论】:

    • 在这个例子中,你可以让 layout2.xml 只包含&lt;TextView /&gt;,没有别的。
    • 没错,可以在 layout2 中使用一个简单的 TextView 代替,但是那将是完全不同的事情,在回答这个问题时作为示例没有用处。
    • 标签一起使用 标签总是很有用的。
    • @Karu:你说得对,在这个例子中合并标签不是必需的,但这只是因为 layout2 中有一个元素。如果 layout2 有多个元素,那么它必须有一个根节点才能成为有效的 XML,这时候合并标签就派上用场了。
    • 那么您将如何指定 是垂直方向还是水平方向?以及如何给出 layout_weight?
    【解决方案2】:

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

    • &lt;merge&gt; 用于优化布局,用于减少不必要的嵌套。
    • 当一个包含&lt;merge&gt;标签的布局被添加到另一个布局中时,&lt;merge&gt;节点被移除并且它的子视图被直接添加到新的父视图中。

    【讨论】:

      【解决方案3】:

      包含标签

      &lt;include&gt; 标签可让您将布局划分为多个文件:它有助于处理复杂 或过长的用户界面。

      假设您使用两个包含文件拆分复杂的布局,如下所示:

      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.xmlinclude2.xml

      请记住,包含文件中的 xml 在渲染时只是转储到您的 top_level_activity 布局中(很像 C 的 #INCLUDE 宏)。

      包含文件是简单的简布局 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" />
      

      看到了吗?没有什么花哨。 请注意,您仍然必须使用 xmlns:android="http://schemas.android.com/apk/res/android 声明 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完全相同的渲染

      那么我们能做些什么呢?

      输入合并标签

      &lt;merge&gt; 标签只是一个虚拟标签,它提供了一个顶级元素来处理这种冗余问题。

      现在 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 已经睡得更好了。

      你现在不开心吗?

      【讨论】:

      • 优秀的描述。
      • 解释得很清楚,应该选作答案
      • 很好,毫无疑问,这应该是公认的答案。
      • 不明白什么......如果外部LinearLayout是垂直的,但include1.xml中的2个文本视图应该是水平的?这种情况下的合并不会保存我想要的布局。可以做些什么呢?
      • @YonatanNir merge 显然不是您所需要的。如果您确实需要扁平化视图层次结构,那么也许您可以使用RelativeLayout 或手动绘制视图
      【解决方案4】:

      使用合并的另一个原因是在 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>
      

      【讨论】:

      • 你是否暗示如果你在你的 XML 文件中使用了一个 RelativeLayout并且你的自定义 ViewGroup 继承自 RelativeLayout,那么就会有两个 RelativeLayout,一个嵌套在另一个中?
      【解决方案5】:

      为了更深入地了解正在发生的事情,我创建了以下示例。查看 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>
      

      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 更新代码以使用合并而不是像 LinearLayout 这样的 ViewGroup。

      <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 工具提供了以下布局层次结构。

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

      【讨论】:

        【解决方案6】:

        根据 android 官方文档,如果没有提供任何 paddingmargin 等,您必须仅使用 merge 代替 FrameLayout

        引用:

        合并根框架 - 如果 FrameLayout 是布局的根并且确实 不提供背景或填充等,可以用合并替换 标签效率略高。

        Docs link

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-06-07
          • 1970-01-01
          • 2013-02-28
          • 2023-03-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-13
          相关资源
          最近更新 更多