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

本文翻译自:What is the purpose of Android's tag in XML layouts?

I've read Romain Guy's post on the <merge /> tag, but I still don't understand how it's useful. 我已经在<merge />标签上阅读了Romain Guy的帖子 ,但我仍然不明白它是如何有用的。 Is it a sort-of replacement of the <Frame /> tag, or is it used like so: 它是<Frame />标签的替代品,还是像这样使用:

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

then <include /> the code in another file? 那么<include />代码在另一个文件中?


#1楼

参考:https://stackoom.com/question/B4mm/Android布局中的-merge-标签的目的是什么


#2楼

<merge/> is useful because it can get rid of unneeded ViewGroups, ie layouts that are simply used to wrap other views and serve no purpose themselves. <merge/>很有用,因为它可以摆脱不需要的ViewGroups,即简单地用于包装其他视图并且本身没有用途的布局。

For example, if you were to <include/> a layout from another file without using merge, the two files might look something like this: 例如,如果您在不使用合并的情况下从另一个文件<include/>布局,则这两个文件可能如下所示:

layout1.xml: layout1.xml:

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

layout2.xml: layout2.xml:

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

which is functionally equivalent to this single layout: 这在功能上等同于这个单一布局:

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

That FrameLayout in layout2.xml may not be useful. layout2.xml中的FrameLayout可能没用。 <merge/> helps get rid of it. <merge/>有助于摆脱它。 Here's what it looks like using merge (layout1.xml doesn't change): 这是使用merge看起来的样子(layout1.xml不会改变):

layout2.xml: layout2.xml:

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

This is functionally equivalent to this layout: 这在功能上等同于此布局:

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

but since you are using <include/> you can reuse the layout elsewhere. 但由于您使用的是<include/>您可以在其他地方重复使用布局。 It doesn't have to be used to replace only FrameLayouts - you can use it to replace any layout that isn't adding something useful to the way your view looks/behaves. 它不必用于仅替换FrameLayouts - 您可以使用它来替换任何未添加对视图外观/行为有用的内容的布局。


#3楼

blazeroni already made it pretty clear, I just want to add few points. blazeroni已经说得很清楚,我只想补充几点。

  • <merge> is used for optimizing layouts.It is used for reducing unnecessary nesting. <merge>用于优化布局。它用于减少不必要的嵌套。
  • when a layout containing <merge> tag is added into another layout,the <merge> node is removed and its child view is added directly to the new parent. 当包含<merge>标记的布局添加到另一个布局中时,将删除<merge>节点,并将其子视图直接添加到新父级。

#4楼

The include tag 包含标签

The <include> tag lets you to divide your layout into multiple files: it helps dealing with complex or overlong user interface. <include>标签允许您将布局划分为多个文件:它有助于处理复杂或过长的用户界面。

Let's suppose you split your complex layout using two include files as follows: 假设您使用两个包含文件拆分复杂布局,如下所示:

top_level_activity.xml : 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>

Then you need to write include1.xml and include2.xml . 然后你需要编写include1.xmlinclude2.xml

Keep in mind that the xml from the include files is simply dumped in your top_level_activity layout at rendering time (pretty much like the #INCLUDE macro for C). 请记住,包含文件中的xml在渲染时被简单地转储top_level_activity布局中(非常类似于C的#INCLUDE宏)。

The include files are plain jane layout xml. include文件是plain jane layout xml。

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

... and include2.xml : ...和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" />

See? 看到? Nothing fancy. 没有什么花哨。 Note that you still have to declare the android namespace with xmlns:android="http://schemas.android.com/apk/res/android . 请注意,您仍然需要使用xmlns:android="http://schemas.android.com/apk/res/android声明android命名空间xmlns:android="http://schemas.android.com/apk/res/android

So the rendered version of top_level_activity.xml is: 所以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>

In your java code, all this is transparent: findViewById(R.id.textView1) in your activity class returns the correct widget ( even if that widget was declared in a xml file different from the activity layout). 在您的Java代码中,所有这些都是透明的:活动类中的findViewById(R.id.textView1)返回正确的窗口小部件(即使该窗口小部件在与活动布局不同的xml文件中声明)。

And the cherry on top: the visual editor handles the thing swimmingly. 顶上的樱桃: 视觉编辑器游戏地处理这件事。 The top level layout is rendered with the xml included. 顶级布局使用包含的xml进行渲染。

The plot thickens 情节变粗

As an include file is a classic layout xml file, it means that it must have one top element. 由于包含文件是经典布局xml文件,因此它必须具有一个顶部元素。 So in case your file needs to include more than one widget, you would have to use a layout. 因此,如果您的文件需要包含多个小部件,则必须使用布局。

Let's say that include1.xml has now two TextView : a layout has to be declared. 让我们说include1.xml现在有两个TextView :必须声明一个布局。 Let's choose a LinearLayout . 我们选择一个LinearLayout

include1.xml : 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>

The top_level_activity.xml will be rendered as: 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>

But wait the two levels of LinearLayout are redundant ! 但等待LinearLayout的两个级别是多余的

Indeed, the two nested LinearLayout serve no purpose as the two TextView could be included under layout1 for exactly the same rendering . 实际上,两个嵌套的LinearLayout没有用处,因为两个TextView可以包含在layout1下,用于完全相同的渲染

So what can we do? 所以,我们能做些什么?

Enter the merge tag 输入合并标记

The <merge> tag is just a dummy tag that provides a top level element to deal with this kind of redundancy issues. <merge>标记只是一个虚拟标记,它提供了一个顶级元素来处理这种冗余问题。

Now include1.xml becomes: 现在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>

and now top_level_activity.xml is rendered as: 现在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>

You saved one hierarchy level, avoid one useless view: Romain Guy sleeps better already. 您保存了一个层次结构级别,避免一个无用的视图:Romain Guy已经睡得更好了。

Aren't you happier now? 你现在不开心吗?


#5楼

Another reason to use merge is when using custom viewgroups in ListViews or GridViews. 使用合并的另一个原因是在ListViews或GridViews中使用自定义视图组。 Instead of using the viewHolder pattern in a list adapter, you can use a custom view. 您可以使用自定义视图,而不是在列表适配器中使用viewHolder模式。 The custom view would inflate an xml whose root is a merge tag. 自定义视图会膨胀其根是合并标记的xml。 Code for adapter: 适配器代码:

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

here is the custom viewgroup: 这是自定义视图组:

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

and here is the XML: 这是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>

#6楼

To have a more in-depth knowledge of what's happening, I created the following example. 为了更深入地了解正在发生的事情,我创建了以下示例。 Have a look at the activity_main.xml and content_profile.xml files. 查看activity_main.xmlcontent_profile.xml文件。

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

In here, the entire layout file when inflated looks like this. 在这里,充气时整个布局文件看起来像这样。

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

See that there is a LinearLayout inside the parent LinearLayout which doesn't serve any purpose and is redundant. 看到父LinearLayout中有一个LinearLayout,它不用于任何目的并且是多余的。 A look at the layout through Layout Inspector tool clearly explains this. 通过Layout Inspector工具查看布局清楚地解释了这一点。

在此输入图像描述

content_profile.xml after updating the code to use merge instead of a ViewGroup like LinearLayout. 更新代码以使用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>

Now our layout looks like this 现在我们的布局看起来像这样

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

Here we see that the redundant LinearLayout ViewGroup is removed. 在这里,我们看到删除了冗余的LinearLayout ViewGroup。 Now Layout Inspector tool gives the following layout hierarchy. 现在,布局检查器工具提供以下布局层次结构。

在此输入图像描述

So always try to use merge when your parent layout can position your child layouts, or more precisely use merge when you understand that there is going to be a redundant view group in the hierarchy. 因此,当您的父布局可以定位子布局时,请始终尝试使用合并 ,或者当您了解层次结构中将存在冗余视图组时,更准确地使用合并

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页