7

As a follow-up to this question, I can't think of any good reason why I shouldn't wrap every included XML layout in a <merge> pair.

Which then leads me to wonder, why didn't the ADT team just make this the default behavior?

Is there any case where one wouldn't want this behavior?

Incidentally, the explanation in the Android documentation of the <merge> tag is worse than the wording in the worst legal agreements:

The <merge /> tag helps eliminate redundant view groups in your view hierarchy when including one layout within another. For example, if your main layout is a vertical LinearLayout in which two consecutive views can be re-used in multiple layouts, then the re-usable layout in which you place the two views requires its own root view. However, using another LinearLayout as the root for the re-usable layout would result in a vertical LinearLayout inside a vertical LinearLayout. The nested LinearLayout serves no real purpose other than to slow down your UI performance.

Romain, where are you?

Community
  • 1
  • 1
Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
  • What happens if you aren't sure what the parent view is? How would you be able to style your children? – hwrdprkns Sep 20 '12 at 18:21
  • @hwrdprkns I'm not sure I understand you. Could you provide an example? This sounds like an answer in the form of a question/comment. By "style", I assume you mean "decide what type of layout they should be enclosed within"? – Jeff Axelrod Sep 20 '12 at 18:32
  • Er, sorry I'm being unclear. What I mean is that `merge` isn't useful when you don't know what the parent `ViewGroup` is, as merge will not/cannot merge between different view groups. e.g. merging something with a `LinearLayout` and a `FrameLayout` is useless. – hwrdprkns Sep 20 '12 at 18:50
  • @hwrdprkns maybe, but is there any harm? I thought the main reason for this tag was to prevent you from needing to surround the included layout file with any type of `ViewGroup`. Even if you did wrap an included `LinearLayout` that was surrounded with a `` pair inside a `LinearLayout` in the including file, I assume it would still enclose the inner `LinearLayout` within the outer one, right? – Jeff Axelrod Sep 20 '12 at 19:05
  • if enclosing and enclosed ViewGroup elements apply same Layout parameters to children ,then yes, auto merge will be useful. But who knows some one might have/look for a reference to enclosed ViewGroup from the code. – S.D. Sep 20 '12 at 19:17
  • @wingman No example that I've seen pf `` shows a `ViewGroup` as its immediate child. – Jeff Axelrod Sep 20 '12 at 19:24
  • There are cases when the merge tag can't be used. If you include a header file with 2 `TextView` that are put in a line(horizontally), you can't add them to a `LinearLayout` that has orientation set to vertical(a quite common case). There is a limitation that you can't inflate a layout file with the `merge` tag without providing a parent `ViewGroup` in the first place(and attach it). Also, as hwrdprkns said, it may be necessary to know the parent of the view to properly position the elements. – user Sep 20 '12 at 19:40
  • @Luksprog but wouldn't it be the parent file that is doing the including that would (and should) determine the specific layout arrangement? What if you later decided that you in fact did have a use for the included file arranged as a vertical layout? Again I think I could use a concrete example demonstrating a problem. – Jeff Axelrod Sep 20 '12 at 19:57

1 Answers1

8

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.

Community
  • 1
  • 1
user
  • 86,916
  • 18
  • 197
  • 190
  • 1
    Thanks so much for the well-thought answer. What I was proposing was that the tag always be there--not that you necessarily eliminate the from the included file as you had done. If you restored it and still kept the merge tag, wouldn't you have the desired behavior? P.S. I had previously read and voted up the relative layout question :) – Jeff Axelrod Sep 21 '12 at 11:54
  • @JeffAxelrod I just tested and from what I see an `include` tag placed in a `RelativeLayout` which inserts a file with the root a `merge` tag(and which has as content the layout from my first `R.layout.actionbar`) doesn't work. Also if you always have the `merge` tag added that would mean the `LayoutInflater` would have to make a xml file pass for a useless element(the `merge` tag) in some situations like the first case from my answer. – user Sep 21 '12 at 12:45
  • Did you follow the [wrapping the includes in a unique parent container](http://stackoverflow.com/a/2548461/403455) advice when you tried this test? Though even if this does fix the problem, I think the extra `ViewGroup` for this workaround would certainly be an argument against doing things this way as opposed to leaving the `ViewGroup` out of the included file. Right? – Jeff Axelrod Sep 21 '12 at 12:58
  • 1
    @JeffAxelrod I didn't used a wrapper `ViewGroup` at first, the important part was to succeed without the need of a wrapper for the `include` element(which also doesn't work after running a second test) as this will add another extra `ViewGroup`. Increasing the depth level by 3 just to insert a `TextView` and a `Button` in a layout seems to much:) – user Sep 21 '12 at 13:06