How to get RelativeLayout working with merge and include?

50,716

Solution 1

There is an issue with the include tag. Check: https://issuetracker.google.com/issues/36908001

To fix it, make sure you overwrite BOTH layout_width and layout_height when including, otherwise everything will be ignored.

Solution 2

See the more highly voted answer below. Mine is woefully outdated


i can address one issue Justin raised: inability of RelativeLayout to manage positioning of an include (at least in this simple case, on a 1.6 emulator)

CommonsWare suggests wrapping the includes in a unique parent container, but does so in order to assist addressing & scoping identically named Views within Justin's includes

Each would have to have a unique parent container, and you would call findViewById() on that container (ViewGroup) rather than on the Activity.

In fact, you also must do it in order to get RelativeLayout to behave as expected:

This works (footer is well positioned):

<RelativeLayout 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/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <LinearLayout android:layout_alignParentBottom="true"
        android:layout_height="wrap_content" android:layout_width="fill_parent">
        <include android:id="@+id/footer" layout="@layout/footer" />
    </LinearLayout>
</RelativeLayout>

This does not (footer is floating at top of screen):

<RelativeLayout 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/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <include android:id="@+id/footer" layout="@layout/footer"
        android:layout_alignParentBottom="true" />
</RelativeLayout>

The bare footer include will not align to bottom of parent, without the surrounding LinearLayout.. I wouldn't call this expected behavior.

Additionally, the WebView appears to attach itself nicely to the header by ID, but I believe this to be illusion, due to it simply flowing below the header vertically. I also tried to set a button right above the footer include, but it got all floaty and wrong, too

RelativeLayout had more problems in 1.5, but i still like it :)

Solution 3

Man, this is old, but it seems to come up at the top of searches, so I'm going to comment.

I think the trick here is that the <merge> tag combined with the <include> tag essentially remove any sort of "parent" view group at that level. So then, who exactly are you asking to "layout_below" someone else? No one. There is no view at that level.

The <merge> tag takes the child views and pops them right into the parent of the <include> tag. You must therefore ask the children in the layout you're including to anchor themselves accordingly.

Solution 4

For positioning to work on RelativeLayout you need to set the layout_* parameters in the include file, not in the main layout file. That way

main_layout.xml

<RelativeLayout
  android:id="@+id/header"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">
   ....
</RelativeLayout>

<RelativeLayout 
  android:id="@+id/footer"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_alignParentBottom="true">
    .....
</RelativeLayout>

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

content_layout.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_above="@id/footer"
    android:layout_below="@id/header" >

    ....
</RelativeLayout>
</merge>

This is obviously not what us developers want, but it's the only solution I've found to avoid duplicating xml

Solution 5

try :

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Share:
50,716

Related videos on Youtube

Justin
Author by

Justin

Updated on July 08, 2022

Comments

  • Justin
    Justin almost 2 years

    I have been trying for a few days now to make my layouts more efficient by converting from using several levels of nested LinearLayouts to one RelativeLayout and have come across a few problems that I haven not been able to find a workaround for...

    I have searched the Android beginners group and this site and have not been able to find anything that would help me solve the problem.

    I read on one of the blogs that you can combine layouts with merge and include tags. So what I have is a main layout file with a RelativeLayout root element. Inside of that I have 5 include tags that reference 5 different xml layout files that each have a merge element for the root (all of my merge files are the same except for the ids in them).

    I am running into two problems, which I will explain after posting a simplified version of my layout code:

    Sample Main Layout File:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/translucent_gray" >
    
        <include 
            android:id="@+id/running_gallery_layout_id"
            layout="@layout/running_gallery_layout" />
    
        <include 
            android:id="@+id/recent_gallery_layout_id" 
            layout="@layout/recent_gallery_layout"
            android:layout_below="@id/running_gallery_layout_id" />
    
        <include
            android:id="@+id/service_gallery_layout_id"
            layout="@layout/service_gallery_layout"
            android:layout_below="@id/recent_gallery_layout_id" />
    
        <include
            android:id="@+id/process_gallery_layout_id"
            layout="@layout/process_gallery_layout"
            android:layout_below="@id/service_gallery_layout_id" />
    
    </RelativeLayout>
    

    Sample included merge file:

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <TextView 
            style="@style/TitleText"
            android:id="@+id/service_gallery_title_text_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="left"
            android:text="@string/service_title" />
    
        <Gallery
            android:id="@+id/service_gallery_id"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_below="@id/service_gallery_title_text_id" />
    
        <TextView 
            style="@style/SubTitleText"
            android:id="@+id/service_gallery_current_text_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/service_gallery_title_text_id"
            android:layout_above="@id/service_gallery_id" />
    </merge>
    

    I am running into two problems:

    1) The android:layout_* attributes seem to be ignored when used in the include tag and all of the merged layouts are displayed on top of each other. According to this post (http://developer.android.com/resources/articles/layout-tricks-reuse.html) "any android:layout_* attribute can be used with the <include /> tag"

    2) Since I couldn't get this working I decided to try adding an android:layout_below attribute to the first TextView item in each merge layout file, meaning that each merge file would be referencing an id from another merge layout file... For the most part this actually worked and my layout looks fine. However, I get an error on one of the android:layout_below attributes saying that it can't find the id I specified... I have double and triple checked the ids to make sure they were correct. The weirdest part is that I used the AutoFill feature to put the id in the attribute in the first place.

    If anyone has any suggestions or workarounds I will be more than happy to try them out. Also, if anyone can think of a way for me to just have one merge xml layout file instead of 5 that would be greatly appreciated. I couldn't find a way to do that because I need to have access to each item in the merge layout files at runtime...

  • Justin
    Justin about 14 years
    I don't want to put them all in one massive layout file because that is harder to manage in the long run... That is why I asked for the possibility to have one main xml with one merge file... because right now I have 5 files with a merge element as the root that have the exact same layout except that the ids are different. I do it that way so that I can access them at runtime. It seems scoping my findViewById() call is what I would want to do. How do you scope that call so that you can include the same layout file multiple times and still be able to access all the components at runtime?
  • CommonsWare
    CommonsWare about 14 years
    Each <include> would have to have a unique parent container, and you would call findViewById() on that container (ViewGroup) rather than on the Activity.
  • Justin
    Justin about 14 years
    Thanks for the response. I'll look into that. In the meantime, (and maybe I'm exposing my ignorance here) wouldn't wrapping each include tag in a parent container defeat the purpose of using RelativeLayout? All the hype of RelativeLayout is to avoid nested layouts...
  • CommonsWare
    CommonsWare about 14 years
    :: shrug :: you're the guy trying to have five copies of the same child widgets all with the same IDs. You would not need the extra containers if you had unique IDs for all your widgets.
  • Justin
    Justin about 14 years
    The only reason I asked about that was to save on space and come up with a good design... I read about layout reusability here: developer.android.com/resources/articles/… and thought it sounded good. When I tried implementing it I ran into some problems. Currently I have 5 layouts that are essentially duplicated except for the id's in them... so I thought layout reusability would be a good candidate. Maybe I'm missing something here, but it seems like RelativeLayout is not all it is touted to be...
  • CommonsWare
    CommonsWare about 14 years
    RelativeLayout is not designed for developers who <include> five identical copies of the same layout XML into the same RelativeLayout instance rather than create custom Views or use an AdapterView.
  • Justin
    Justin about 14 years
    Wow... thanks for being so incredibly helpful. Generally your responses are pretty helpful so I don't know if you are just having a bad day or what, but I was simply trying to gain a better understanding of the concepts behind RelativeLayout, the include tag, and the merge tag, based on articles I have read and trying to find an appropriate solution to the layout I want to achieve.
  • CommonsWare
    CommonsWare about 14 years
    You chose to blame RelativeLayout broadly ("All the hype of RelativeLayout", "RelativeLayout is not all it is touted to be") because it does not handle your specific case. You copped a 'tude -- please don't be surprised when people react to that. And while my most recent comment is high on snark, the points are still valid. 5 of your layout might not fit on QVGA, and I have no idea if 5 is a guaranteed value or might change -- either case, switching to rows in a ListView might be better. And, for true reuse, creating a custom View class trumps <include>.
  • Justin
    Justin about 14 years
    "And, for true reuse, creating a custom View class trumps include" Agreed. I just didn't want to do that if there was a relatively easy way to use basic layouts... "You copped a 'tude -- please don't be surprised when people react to that. And while my most recent comment is high on snark, the points are still valid" I copped a 'tude because I felt that you did in your responses. "switching to rows in a ListView might be better." Listview won't work with the look of my app. My app on the market is AppSwipe! if you want to know what I'm doing...
  • mikołak
    mikołak over 12 years
    This is a better solution than the accepted one, as it avoids creating an otherwise superfluous layout object. Also, it sucks how according to the Android devs this is a-OK.
  • teoREtik
    teoREtik about 12 years
    This is really easier, less hardcoded, and more optimized solution than packing <include/> into another layout. Think what would you do if you would work with lists.
  • QED
    QED about 12 years
    Why no upvotes? This worked for me. I'm not in love with sticking parameters into an object which may be included into a layout which doesn't need them, but if it's a LinLay it seems that RelLay's layout_* are just getting ignored. Am I missing something?
  • Felipe Caldas
    Felipe Caldas over 11 years
    This works much better than accepted answer. Many thanks! And... c'mon google, fix this issue already, this is BS! :)
  • Jeff Axelrod
    Jeff Axelrod over 11 years
    Hey Macarse, long time no talk. Hey, I tried the fix, but it didn't work in my case with a relative layout that included a single EditText surrounded by a <merge> pair.
  • Macarse
    Macarse over 11 years
    @JeffAxelrod: Impossible to answer without looking to the code, please create a new question with all the information.
  • Jeff Axelrod
    Jeff Axelrod over 11 years
    @Macarse sorry, I wasn't asking for a solution, just pointing out that the workaround you suggested doesn't work in my case. My included file contains a single UI element. I'm able to get the <include>/<merge> pair to work if I surround each single <include> with any type of layout. Something about this feels like there could be a rational justification for the apparent tooling "bug"--just the documentation is defective. I set the included file single element's dimensions to fill_parent and then the ultimate dimensions I'd like are in the parent element. This makes sense to me.
  • Jayshil Dave
    Jayshil Dave about 11 years
    I increased it by 1 and then decreased back seeing the comment by @Macarse, thats the correct way to do it.
  • Rafael Nobre
    Rafael Nobre almost 11 years
    @JeffAxelrod , the LayoutInflater source code shows that id, visibility and layout_* tags overriding are not applied when the root element is a merge tag, unfortunately. As you can't have a View as the xml root, we must have an extra ViewGroup there...
  • Daniel Smith
    Daniel Smith about 10 years
    Please remove this misleading answer :(
  • dum4ll3
    dum4ll3 almost 10 years
    It just didn't work for me. I have both layout_width and layout_height setted on my <include>. I tried also setting layout_width and layout_height on my <merge> but without success. What am I missing ?
  • lilbyrdie
    lilbyrdie almost 10 years
    Now to get the preview working in the graphical layout view. ;)
  • desseim
    desseim about 9 years
    It won't work if the included layout's root layout is of merge type. Seemingly the include tag parameters are only applied to the included layout's root layout, not its child(ren) in case of merge. Maybe because there could be several children at the same level (right below the merge tag) and it would get ambiguous how to apply the include tag parameters then.
  • Edward Falk
    Edward Falk almost 9 years
    Note also: if the layout parameters come from a style, then you can override the style.
  • SpaceMonkey
    SpaceMonkey over 8 years
    Now the question is, why is Android such a pile of garbage?
  • Silvia H
    Silvia H over 4 years
    this will create another nested layer which is not the optimal solution