0

I'm getting a little frustrated with the Layout support in ADT and Eclipse. It's Swing all over again. Basically I'm trying to implement a simple layout like so: RelativeLayout

+----------------+
|                |
| FrameLayout    |
|    ImageView   |
|                |
|                |
|                |
+----------------+
| LinearLayout   |
|    TextView    |
|    Buttons     |
+----------------+

The RelativeLayout and FrameLayout are used with the ImageView for some pan/zoom listener support I wrote.

The problem I'm having is the LinearLayout (bottom controls) won't say at the bottom of the window no matter what I try. I'll simply paste what I've got below. Note that the FieldView is an extension of an ImageView. The image displays fine, its the LinearLayout that wants to be displayed on top of the FrameLayout that's in the way.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fieldcontainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false"
    android:orientation="vertical" >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <com.test.FieldView
            android:id="@+id/field"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </FrameLayout>

    <LinearLayout
        android:id="@+id/controls"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/buttonAddNode"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Add node" />

        <TextView
            android:id="@+id/textViewNodeInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" />
    </LinearLayout>

</RelativeLayout>

Thanks in advance for any advice. Maybe I misused a RelativeLayout parameter in my previous tries.

UPDATE Thanks for the fast response! I couldn't comment with code below, so here's an update I've tried:

<FrameLayout
    ...
    android:layout_above="@+id/controls">

;previously and I get the following exception at runtime:

Unable to start activity ComponentInfo{com.test.MainActivity}:
    java.lang.ClassCastException: android.widget.LinearLayout

The line throwing the exception is in my MainActivity.OnCreate():

view = (FieldView) findViewById(R.id.field);

It looks like adding layout_below caused an ID mismatch? Finding R.id.field is returning the ID for the wrong LinearLayout, which I naturally can't cast.

Note that without the layout_above, this works without any problem, just a broken layout. Again, FieldView is just an extension of an ImageView. I store my touch listeners and some other stuff in there.

dubmojo
  • 6,660
  • 8
  • 41
  • 68

3 Answers3

1

Add the android:layout_alignParentBottom="true" property to the LinearLayout in your layout. If you want the FrameLayout to not go under the LinearLayout, then your layout should look like this:

<RelativeLayout>

    <LinearLayout  android:id="@+id/ll" android:layout_alignParentBottom="true" />
    <FrameLayout android:layout_above="@id/ll" android:layout_alignParentTop="true"/>
</RelativeLayout>
user
  • 86,916
  • 18
  • 197
  • 190
1

You simply wrap it all in a relative layout and anchor the linear layout to the bottom using. android:layout_alignParentBottom="true"

To keep the controls from being over-run by the stuff above it, lay them out above using android:layout_above="@id/controls".

LIke so:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fieldcontainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false" >
    <!-- Your other stuff -->

    <LinearLayout
        android:id="@+id/controls"
        android:layout_width="match_parent"
        android:layout_height="wrap_contgnt"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true">
    <!-- Your control stuff -->
    </linearlayout>
</relativelayout>

Note that you must define an id (@+) before you can simply reference (@) it. So you can either move the controls view to occur in the XML file before anything else, or use "@+" in the location reference that comes first and then just use @ in the view defineintion.

So you would have android:layout_above="@+id/controls" and then the other line would be android:id="@id/controls".

And changing your layout would have no effect on the id's present within it unless you changed them.

Barak
  • 16,318
  • 9
  • 52
  • 84
  • Thanks but I'm getting an exception when using layout_below, at runtime the IDs aren't correct. See my update above. Thanks. – dubmojo Nov 25 '12 at 21:24
  • Updated my answer, hopefully it will help point you in the right direction. – Barak Nov 25 '12 at 22:37
1

You're using a RelativeLayout but then not using any attribute of it to control positioning.

As you have it, you might as well be using a vertical LinearLayout. You have also set the heights of your FrameLayout and LinearLayout to match_parent which means you have devolved all responsibility for laying out these view groups to the parent view, the RelativeLayout.

When Android lays out a layout, it does two top down passes. First, the parent asks it's children how big they want to be. It passes it's own size to the children to do this. So each of your FrameLayout and LinearLayout will say that they want to be the same size as the parent. The RelativeLayout has no rules to determine how these controls should relate to each other and so will try to make each one as large as possible, given it's contents. The second pass is top down when the parent tells each child how big to make itself and renders the layout.

So, to fix your problem, I would use one of two approaches (there are more but for a simple layout like this, you shouldn't need them).

  • Use RelativeLayout with layout_below to force the LinearLayout to always be below the FrameLayout, regardless of sizing or by using any of the various parent alignment attributes.

  • For more precise control, use a LinearLayout with layout weights to both position and precisely size each of it's children. Should you take this approach, remember to set heights to 0dip and use view "struts" to position.

My personal preference is to use LLs since I have found them to be rock solid and easy to get precise layout sizing and positioning (although I sometimes to have to extend them and mess with their onMeasure() methods but that's an Android fragmentation issue, not directly related).

What does android:layout_weight mean?

Community
  • 1
  • 1
Simon
  • 14,407
  • 8
  • 46
  • 61
  • If you use `layout_below` on the `LinearLayout` you have the chance to throw it out of the screen if the `FrameLayout` gets as big as the screen. – user Nov 25 '12 at 20:35
  • @Luksprog Yep, very true. As I say, I avoid Relatives unless I need relative positioning or alignment. – Simon Nov 25 '12 at 20:41
  • Thanks but I'm getting an exception when using layout_below, at runtime the IDs aren't correct. See my update above. Thanks. – dubmojo Nov 25 '12 at 21:24
  • RelativeLayouts process their children top down. Use layout below on your LinearLayout rather than layout_above on your FrameLayout. However, do see Luksprogs' comment above. If I was doing this, I would definitely choose LinearLayout and use layout weights. You can set a weight sum of 1000 and get .1% accuracy in your positioning i.e. pixel perfect. IMHO ,relative layouts tend to be great for form type layouts and complex arrangements where views must move around as resolutions and orientations change but for simple split screens and so on, I find LLs much easier to work with. – Simon Nov 25 '12 at 21:49