9

I'd like to figure out how to reuse or "alias" layouts with the least boilerplate code.

It seems that the Android documentation about layout aliases is incorrect, and certainly appears inconsistent. This section of documentation shows the following layout file as an example:

<resources>
    <item name="main" type="layout">@layout/main_twopanes</item>
</resources>

If I try to compile this, I get an Attribute is missing the Android namespace prefix error. Even after adding the namespace to the resources element, I get error: Error: String types not allowed (at 'type' with value 'layout').

Elsewhere in the Android documentation, they show a different and seemingly inverted and incorrect way to alias layouts:

To create an alias to an existing layout, use the element, wrapped in a <merge>. For example:

<?xml version="1.0" encoding="utf-8"?>
<merge>
    <include layout="@layout/main_ltr"/>
</merge>

Running this results in the following error in LogCat E/AndroidRuntime(1558): android.view.InflateException: <merge /> can be used only with a valid ViewGroup root and attachToRoot=true. So this error seems to reinforce the fact that this <include> <merge> pair must be a mistake, because it requires an unnecessary parent View.

Lastly there's the <merge> documentation, which seems to contradict the former direction, making no mention of the inverted form of a top-level <merge><include/></merge>.

To avoid including such a redundant view group, you can instead use the element as the root view for the re-usable layout. For example:

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

    <Button
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="@string/add"/>

    <Button
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="@string/delete"/>

</merge>
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
  • 1
    The [first method](http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters) seems to compile and work fine for me. Do you have the latest SDK Tools installed? – Joe Oct 10 '12 at 01:48
  • @Joe thanks for verifying this! It ends up I didn't read the instructions carefully. I did what I thought I should do--put the layout.xml inside the `layout-large` rather than `values-large` folder. – Jeff Axelrod Oct 10 '12 at 02:04

1 Answers1

8

The first technique works, you just have to put your <resources> file in the correct folder. It should be in the values folders not the layout folders as you might when reusing layouts via <include>.

For instance, suppose you have a layout named editor.xml that lives in the layout folder. Suppose you want to use a specialized layout on small and normal screen sizes. If you didn't care about repeating yourself, you would just copy and paste this layout into the layout-small and layout-normal folders and name it editor.xml in each folder. So you'd have three files named editor.xml.

If you don't want to repeat yourself, you would place the specialized layout in the main layout folder and name it, say, compact_editor.xml. Then you'd create a file named layout.xml in the values-small and values-normal folders. Each file would read:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="editor" type="layout">@layout/compact_editor</item>
</resources>

I've filed a documentation issue about the other two problems.

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
  • 1
    "this inverted use of merge seems to be undocumented otherwise" -- the reason the `` is needed is that `` is not supported as a root element, for some reason. Otherwise, this use of `` is normal. `` says "here's a bunch of widgets and containers that need to go in some indeterminate parent", with `` serving as the root XML element (since XML needs one). In this case, the "bunch of widgets and containers" happen to be stored in another layout resource, hence the ``. – CommonsWare Oct 09 '12 at 16:18
  • @CommonsWare Sounds reasonable, but this is your interpretation based on your understanding of `` not its [actual documentation](http://developer.android.com/training/improving-layouts/reusing-layouts.html#Merge). – Jeff Axelrod Oct 09 '12 at 16:26
  • Well, the actual documentation isn't exactly going to win a prize, but there is nothing in there that I see that contradicts what I wrote. – CommonsWare Oct 09 '12 at 16:27
  • @CommonsWare Okay, note [this new issue](http://code.google.com/p/android/issues/detail?id=38312): Support as a top-level element so we don't need to surround it with – Jeff Axelrod Oct 09 '12 at 16:32
  • 1
    @CommonsWare You're probably tired of discussing this, but after actually testing the second option in a `ListView`, the inflater fails. See my question edit: Running this results in the following error in LogCat E/AndroidRuntime(1558): android.view.InflateException: can be used only with a valid ViewGroup root and attachToRoot=true. So this error seems to reinforce the fact that this pair must be a mistake, because it requires an unnecessary parent View. – Jeff Axelrod Oct 10 '12 at 01:12
  • The `` definitely works with `setContentView()`, as I tested that before commenting. I can see where it might not work with `inflate(R.layout.alias, parent, false)`. – CommonsWare Oct 10 '12 at 10:10
  • @CommonsWare So first, this approach can't work from a `ListView` row. But in defense of the documentation, they didn't advertise that it would. More importantly though, doesn't this then create a possibly unnecessary layer of `ViewGroup` even when it does work? There's the parent's `ViewGroup` as well as the one in the included file. I was pleased to learn that the first resources strategy actually does work--it was my error. – Jeff Axelrod Oct 10 '12 at 20:00
  • "More importantly though, doesn't this then create a possibly unnecessary layer of ViewGroup even when it does work? There's the parent's ViewGroup as well as the one in the included file." -- it should not add any more layers than you would have in referring to the original resource. All that being said, any chance I can con you into adding an update to your question with the proper syntax for what you got working? I haven't seen that approach before. Thanks! – CommonsWare Oct 10 '12 at 20:21
  • Thanks! Though in the particular scenario you outlined, the simpler solution would be to have `res/layout/editor.xml` (for small/normal) and `res/layout-large/editor.xml` (for large/xlarge). Then, you do not need the alias. That being said, layout aliases will be very useful when mixing and matching other resource set qualifiers, like the `-swNNNdp` stuff. – CommonsWare Oct 16 '12 at 16:45
  • @JeffAxelrod did you mean to say `values-small` and `values-normal`? Otherwise that is identical to the original documentation, no? I will do an edit to this answer to use their example with what you propose is the solution in the first paragraph (and seems to work for my project) – Dandre Allison Dec 17 '12 at 18:26
  • @DandreAllison yes, thanks, it should have read `values`, not `layout`. I've corrected it. If you'd like to add anything else, please do so in a new answer or comments. Thanks! – Jeff Axelrod Dec 17 '12 at 20:31