What Is the Purpose of Android's <Merge> Tag in Xml Layouts

What is the purpose of Android's merge tag in XML layouts?

<merge/> is useful because it can get rid of unneeded ViewGroups, i.e. layouts that are simply used to wrap other views and serve no purpose themselves.

For example, if you were to <include/> a layout from another file without using merge, the two files might look something like this:

layout1.xml:

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

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. <merge/> helps get rid of it. Here's what it looks like using merge (layout1.xml doesn't change):

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

What is the difference between include tag and merge tag in android

Check This Difference between include and merge

How does merge tag work internally

If an <include> tag includes both layout_width and layout_height, it will override all of the root view's (of the file it is including) layout parameters.

A merge is a way to avoid an additional depth in the view hierarchy - an XML layout file must only have 1 root - so it must either have a single View, a ViewGroup which can include additional Views or a <merge>. When you're including a layout with multiple Views inside a ViewGroup, you may be adding extra unneeded complexity to your hierarchy - for example, 2 vertically orientated LinearLayouts may not be required. The merge allows you to remove the extra ViewGroup, and merge its Views into the ViewGroup where it is included.

Including a layout with a merge with a single View is the same as just including a layout with the single View directly, except that with the merge there isn't a 'root view' of the layout, so the include will not override its layout parameters (I believe).

When a View or ViewGroup is inflated into a ViewGroup which doesn't support the layout parameters that the child specifies, the parameters are just dropped. This occurs when including or inflating (using a layout inflater).

If a child View or ViewGroup is added to a ViewGroup (using addView), it is possible that the child already has layout parameters assigned, and if they aren't compatible with the ViewGroup it is being added to, it may result in a class cast exception later on during measuring / layout.

Your second question is to do with RelativeLayouts layout rules, and its behaviour can be defined using android:layout_alignWithParentIfMissing

How does merge tag work internally

If an <include> tag includes both layout_width and layout_height, it will override all of the root view's (of the file it is including) layout parameters.

A merge is a way to avoid an additional depth in the view hierarchy - an XML layout file must only have 1 root - so it must either have a single View, a ViewGroup which can include additional Views or a <merge>. When you're including a layout with multiple Views inside a ViewGroup, you may be adding extra unneeded complexity to your hierarchy - for example, 2 vertically orientated LinearLayouts may not be required. The merge allows you to remove the extra ViewGroup, and merge its Views into the ViewGroup where it is included.

Including a layout with a merge with a single View is the same as just including a layout with the single View directly, except that with the merge there isn't a 'root view' of the layout, so the include will not override its layout parameters (I believe).

When a View or ViewGroup is inflated into a ViewGroup which doesn't support the layout parameters that the child specifies, the parameters are just dropped. This occurs when including or inflating (using a layout inflater).

If a child View or ViewGroup is added to a ViewGroup (using addView), it is possible that the child already has layout parameters assigned, and if they aren't compatible with the ViewGroup it is being added to, it may result in a class cast exception later on during measuring / layout.

Your second question is to do with RelativeLayouts layout rules, and its behaviour can be defined using android:layout_alignWithParentIfMissing

Simple example of merge and include usage in Android XML-layouts

some_activity.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">

// some views

<include layout="@layout/view_part"/>

// probably more views

</LinearLayout>

view_part.xml:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

// the views to be merged

</merge>

Why shouldn't we wrap every included Android XML layout in a merge pair?

The main purpose of the include tag(the way I see it) is to allow the developer to create reusable xml components to be used multiple times in the same activity or/and across many activities in an app. In order for that reusable component to be really useful it needs to be self contained and with a minimum of outside connections as posible. From my point of view, if you were to use the merge tag in each included layout this will reduce the usefulness of the include tag overall. Here is an example why I think this will happen:

Consider that you want to implement a reusable ActionBar xml component to embed in each of your activities. It will contain a TextView and a Button placed horizontally. A layout to do this would be:

R.layout.actionbar

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<TextView
android:id="@+id/actionbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<Button
android:id="@+id/actionbar_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

Now suppose you have two activities in your app, one where the root view is a LinearLayout(orientation vertical) and one where the root view is a RelativeLayout. The layout above could easily be included in the LinearLayout(just put it where you want), the same will be possible with the RelativeLayout, of course taking in consideration the current elements from that RelativeLayout(keep in mind that you must set the layout_width/height(for example, replicated from the included layout's root) for the include tag in order for the other layout_* attributes to be considered).

Now take in consideration your proposal. The layout will become:

<merge xmlns:android="http://schemas.android.com/apk/res/android" >

<TextView
android:id="@+id/actionbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<Button
android:id="@+id/actionbar_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</merge>

Looking at our ActionBar layout doesn't tell us that much, you just have a TextView and a Button to be included somewhere. Now consider the activity where the root is the vertical orientated LinearLayout. The layout R.layout.actionbar can't be simply included as this will break our ActionBar, we need to add an extra LinearLayout(with orientation horizontal) for our layout to make it look as desired. As you can see you are in the situation above(layout without the merge tag), but now you have to remember to wrap the included layout in a LinearLayout with orientation horizontal where ever the parent root is a LinearLayout with orientation vertical.

Things get even worse when the root is a RelativeLayout, you can't simply use the include tag with merge in a RelativeLayout(a nice question to read How to get RelativeLayout working with merge and include?). The option is, again, to embed the include in a LinearLayout which puts you in the situation without the merge tag(but now adding more problems than solving). Also reading the last part from this link http://code.google.com/p/android/issues/detail?id=2863 may reveal other bugs with the include tag.

As you can see from my example above, having the merge tag by default could result in some problems in certain situation. Also the current system represents a more consistent way to work with layouts(you would create the layout for the include tag like you create normal layouts with a root View). Also, the merge tag is an optimization and I don't think you should try to optimize until you start to see some performance issues(or you really want to squeeze every last drop of performance, at the cost of complexity). The majority of apps will be just fine with the current system, a three-four level deep layout with a decent amount of views could live without the merge optimization with no problems at all.

Another problem with the merge tags is that a inflated layout that has merge as its root is required to attach itself to a parent when inflated. If you were to inflate the R.layout.actionbar layout then you would have to attach it to a parent:

View actionBar = getLayoutInflater().inflate(R.layout.actionbar, root, true);

I don't know if this is a real limitation, maybe in some rare situation it could be a deal breaker.

Just my opinion about the include - merge pair use.

Can some clarify usage of include and merge

Yes you understood it correctly. merge is used as pseudo parent element to reduce the number of levels in view trees.
Just check this link, it gives very good explanation of merge.

In your header file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<include
android:id="@+id/header"
layout="@layout/top"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>

<LinearLayout> doesn't make any difference when your file is included in other file you mentioned. So it's a good thing to use merge instead.

Since in XML you must use a single parent element and the rest of the XML elements should be included in it, you should use merge as single parent element and can avoid adding unnecessary parent layout.

Just avoid 'merge' when you want to apply a layout differently than layout is defined in file in which your content is inclded.

How can I access views from layout containing merge tag is included in another layout?

I wrote an article about using ViewBinding with <merge> tag which you can find here

Basically, what you need to do is

  • Don't give <include> tag any ID.
  • Call bind() method of generated merge layout binding, passing the root view of the layout you included your layout in.
  • Access your view from the object of merge binding

For example, you have merge_view.xml so you'll have MergeViewBinding class generated and this is how you will access the view from this layout.

class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityTestBinding.inflate(layoutInflater)
val mergeBinding = MergeViewBinding.bind(binding.root)
setContentView(binding.root)
mergeBinding.label.text = "New text"
}
}


Related Topics



Leave a reply



Submit