views:

969

answers:

2

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"&gt;
    <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 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...

Thanks in advance!

A: 

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.

My guess is that you cannot reference, from layout rules, android:id attributes that are defined on <include> elements, only ones that are on "real" widgets and containers.

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.

Simple: put them all in one file.

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

Whether you have one <include> element or 1,000, all of the contents should be accessible at runtime. One exception is if you have duplicated android:id attributes -- you would need to properly scope your findViewById() calls to get the right one, just like you have to when getting widgets from a ListView row.

If you can create a sample project that uses 2+ merge files where you can demonstrate that the contents are not accessible at runtime, let me know.

CommonsWare
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?
Justin
Each `<include>` would have to have a unique parent container, and you would call `findViewById()` on that container (`ViewGroup`) rather than on the Activity.
CommonsWare
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...
Justin
:: 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.
CommonsWare
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: http://developer.android.com/resources/articles/layout-tricks-reuse.html 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...
Justin
`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`.
CommonsWare
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.
Justin
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>`.
CommonsWare
"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...
Justin
+1  A: 

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 :)

alienjazzcat