Android Layout Trick #2: Include to Reuse

Android comes with a wide variety of widgets, small visual construction blocks you can glue together to present the users with complex and useful interfaces. However applications often need higher level visual components. A component can be seen as a complex widget made of several simple stock widgets. You could for instance reuse a panel containing a progress bar and a cancel button, a panel containing two buttons (positive and negative actions), a panel with an icon, a title and a description, etc. Creating new components can be done easily by writing a custom View but it can be done even more easily using only XML.

In Android XML layout files, each tag is mapped to an actual class instance (the class is always a subclass of View.) The UI toolkit lets you also use three special tags that are not mapped to a View instance: <requestFocus />, <merge /> and <include />. The latter, <include />, can be used to create pure XML visual components. (Note: I will present the <merge /> tag in the next installment of Android Layout Tricks.)

The <include /> does exactly what its name suggests; it includes another XML layout. Using this tag is straightforward as shown in the following example, taken straight from the source code of the Home application that currently ships with Android:

<com.android.launcher.Workspace
    android:id="@+id/workspace"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"

    launcher:defaultScreen="1">

    <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
    <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
    <include android:id="@+id/cell3" layout="@layout/workspace_screen" />

</com.android.launcher.Workspace>

In the <include /> only the layout attribute is required. This attribute, without the android namespace prefix, is a reference to the layout file you wish to include. In this example, the same layout is included three times in a row. This tag also lets you override a few attributes of the included layout. The above example shows that you can use android:id to specify the id of the root view of the included layout; it will also override the id of the included layout if one is defined. Similarly, you can override all the layout parameters. This means that any android:layout_* attribute can be used with the <include /> tag. Here is an example:

<include android:layout_width="fill_parent" layout="@layout/image_holder" />
<include android:layout_width="256dip" layout="@layout/image_holder" />

This tag is particularly useful when you need to customize only part of your UI depending on the device’s configuration. For instance, the main layout of your activity can be placed in the layout/ directory and can include another layout which exists in two flavors, in layout-land/ and layout-port/. This allows you to share most of the UI in portrait and landscape.

Like I mentioned earlier, the next installment will explain the <merge />, which can be particularly powerful when combined with <include />.

32 Responses to “Android Layout Trick #2: Include to Reuse”

  1. Stephen Tallent says:

    Great stuff and will definitely help me clean up my project a bit. Question, though, while I have seen the reqestFocus tag referenced in the Android documentation, i must have overlooked the include and merge tag. Where would info about them be found in the official Android dox?

  2. Al says:

    Nice, I really enjoy reading these Android programming posts *thumbs up*

  3. Matthias says:

    What happens if you specify an android:id in when including a merge view?

    The problem is that neither are IDs of included views visible in the file that includes them, nor are IDs visible across different included files.

    However, you need to refer to IDs of included files all the time when using RelativeLayout. I noticed that you can fix this by giving the include tag an ID to make this ID visible, but semantically, it doesn’t make sense to me at all.

    Interestingly enough, the included views can see IDs of other elements in the file that includes them, but not vice versa…

  4. Romain Guy says:

    “The problem is that neither are IDs of included views visible in the file that includes them, nor are IDs visible across different included files.”

    Yes they are.

    “However, you need to refer to IDs of included files all the time when using RelativeLayout. I noticed that you can fix this by giving the include tag an ID to make this ID visible, but semantically, it doesn’t make sense to me at all.”

    No, you can just use the id defined inside the included file.

    “Interestingly enough, the included views can see IDs of other elements in the file that includes them, but not vice versa…”

    Yes they can.

  5. Matthias says:

    Then why does the Android compiler break with an error: “Unknown resource” when referencing IDs of included views?

  6. Romain Guy says:

    Ids must be declared and referenced in the right order. It applies even when you don’t use includes. To reference an id inside an included layout use the @+id notation or declare the id in your values/ directory. To reference an id outside of an included layout, also use the @+id notation.

  7. Matthias says:

    So I basically create the same ID twice (doesn’t “+” mean “create this”?), once in the included file and once in the including file? I wasn’t aware that this is even possible. Doesn’t that imply I would also be able to reference an ID that is not attached to any view, simply by doing something like this:

    someDanglingId now exists, but it doesn’t point anywhere.

  8. Matthias says:

    Okay, that XML snippet didn’t make it through… What I meant was using the +-notation to create an ID in an attribute (e.g. android:layout_below=”@+id/something”) without creating a view which actually has that ID beforehand. Wouldn’t the ID “something” not point to anything in that case?

  9. Matthias says:

    I also noticed that overriding layout parameters as mentioned in this post actually never works for me, even with very simple layouts. For instance, I am trying to include two linear layouts into a frame layout, but for landscape mode, I want the included layouts’ width to wrap_content (and use weights), and in portrait mode I want them to fill_parent (and not use weights).

    Overriding those layout parameters in the include tag however has no effect at all. The included layouts will expose only the behavior as defined in the layout files themselves, not the one defined in the include tag.

    Same thing when including relative layouts. Even with that @+id notation, putting a layout_below in the include tag has no effect whatsoever.

    I raised an issue for this on the tracker.

  10. Romain Guy says:

    I don’t know what you’re doing exactly but I just run our tests and wrote a simple app using SDK 1.5r1 and the Eclipse plugin and it works just fine.

  11. Matthias says:

    Hi Romain,

    I wrote a trivial test app which exposes the problem I was referring to. Since you mentioned that the layout tag works fine for you, could you send me that test app of yours so I can see if it works on my setup?

  12. Hi i’m also having problems with include and RelativeLayouts.
    http://groups.google.com/group/android-developers/browse_thread/thread/e6e4f02b4a620aa

    Romain, could you post or send your test layout app so we can see how you can position layouts that are added via include ?

  13. Michael Krause says:

    Hi Romain,

    Hey – thanks for the great article! I was just wishing there was a way to do this and came across your blog. Very helpful.

    Regards,

    - Mike

  14. Harry Mitchell says:

    Hello,

    I wrote a test app to test this functionality. The LinearLayout(s) are added but the text on the button is not displayed and the button. What am I missing?

    Here is the LinearLayout with the button:
    [code]

    [/code]

    Here is the other included LinearLayout:
    [code]

    [/code]

    Here is the LinearLayout that includes the other two:
    [code]

    [/code]

    Thanks,
    Harry

  15. Harry Mitchell says:

    Let’s try this again…

    Hello,

    I wrote a test app to test this functionality. The LinearLayout(s) are added but the text on the button is not displayed and the button. What am I missing?

    Here is the LinearLayout with the button:

    Here is the other included LinearLayout:

    Here is the LinearLayout that includes the other two:

    Thanks,
    Harry

  16. Harry Mitchell says:

    How do I include formatted xml with my post?

  17. hron84 says:

    @Hary Mitchell: Use pastebin.com or pastie.org

  18. ajaykasam says:

    Hi,

    In the above sited example.

    How can I enable a scrollbar for the three included workspace screen, to scroll across the three screens.

    Thanks and Regards,
    Ajay Kasam

  19. jhoffman says:

    I’m having the same issue that Matthias discussed a year ago.

    Would it be possible to get some sample code of how to properly set up the ids in values?

  20. jyhsu says:

    I also have a similar question. How do you get the correct view in the code with findViewById when you are including a layout for three times? The sample here is overwriting the layout id, not the id of the views IN this layout. For example, if the workspace_screen.xml looks like this:

    Do I end up with three TextViews with id firstText, and another three with secondText? Isn’t there an id collision? Then how do I find the secondText TextView in the third included layout with findViewById? What should I input in the findViewById method?

  21. swinefeaster says:

    if i want to reuse a definition can i do so using #include? Or do i need to wrap that button in a LinearLayout, which would violate you Trick#3 Optimize section ;)

    thanks.

  22. dev says:

    layout_margin and other layout_* parameter does not work.

  23. Henrik says:

    The include problem has a workaround, see comment #9 at http://code.google.com/p/android/issues/detail?id=2863#c9

    In short, you need to specify BOTH layout_width and layout_height if you want to override any layout_* attributes.

    Noone seems to want to fix the issue, though…