8
votes

I imagine this should be a fairly easy one to answer, if you understand XML Layouts better than I do that is. I don't seem to get what I was thinking I should when using the match_parent layout_height.

I have a LinearLayout root element with android:orientation="vertical". Inside this LinearLayout I want three elements: - TextView - ListView - TextView

For both the TextViews I set android:layout_height="wrap_content" so that they will be only as tall as is necessary to display their contents. The thing is, I want the one TextView to sit at the top of the form, the other one to sit at the bottom of the form while the ListView fills up whatever space is available on the form. So here is what my xml layout looks like:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="30sp"
    android:text="Top TextView" />

<ListView
    android:id="@+id/listView_Species"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="30sp"
    android:text="Bottom TextView" />

But it doesn't work. Here's what I get. I've selected the ListView so that it will be highlighted. Notice how it extends all the way to the bottom of the form, pushing the bottom TextView off the form. XML Layout 1

When I change the layout_height property of the ListView to some fixed value, like 180dp, this is what the form looks like. I'm just posting this to prove that the bottom TextView is there but I still don't know how to get it to be fixed to the bottom of the screen while the ListView takes up whatever space remains, but in between the two TextViews.

XML Layout 2

Thanks in advance.

3

3 Answers

19
votes

While the other answers try to fix your problem (which they don't actually--they suggest you do something that looks similar but may or may not look good on different devices), no one has filled in the gaps in your knowledge of LinearLayouts and match_parent. And these gaps are very common--Google's documentation is still far below stellar.

First, how do Views work within a LinearLayout? Let's go through the process of drawing a LinearLayout, using orientation="vertical" for simplicity.

  1. Examine the height of the first child of the LinearLayout (LL for short). If the height is match_parent or fill_parent (old name for the same thing) then the height of the View is stretched to fill the entire viewing area. If the height is wrap_content, then measure the vertical space the View takes and use that space for the View. If the height is a non-zero number, use exactly that many pixels for the View's height (may clip if too small). If the height is 0 see below.
  2. Put the next view below the view in 1. Check its height and act accordingly.
  3. Continue for all the Views. If a View is pushed off the bottom, go ahead and stop calculating because no one will see it or any succeeding Views (assuming no ScrollView).
  4. If the height of a View is 0, check it's gravity. This requires a second pass, storing the gravity of all the views and then allocating their heights proportionally. As you can guess, the second pass doubles the time layout takes, which isn't significant for simple layouts.

Explanation of your example: The first child of the LL (the first TextView) is measured and takes a certain amount of pixels. Then your ListView takes all the remaining space (via match_parent). And then your second TextView is not drawn at all as it's off the bottom of the screen. Which is pretty much what you observed, but now you understand why.

Solution: Use RelativeLayout. Works perfectly in this case.

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/top_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:textSize="30sp"
        android:text="Top TextView" />

    <TextView
        android:id="@+id/bottom_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:textSize="30sp"
        android:text="Bottom TextView" />

    <ListView
        android:id="@+id/listView_Species"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/top_tv"
        android:layout_above="@id/bottom_tv"
        />
</RelativeLayout>

The RelativeLayout tells the layout inflater to draw the first TextView at the top, then draw the second TextView at the bottom, and then fill the rest of the space with your ListView. I believe this is exactly what you want.

Welcome to Android. You'll be using this pattern a LOT!

12
votes

Change the ListView height to 0dp and add weight=1
i.e.:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="30sp"
    android:text="Top TextView" />

<ListView
    android:id="@+id/listView_Species"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="30sp"
    android:text="Bottom TextView" />
3
votes

use android:layout_weight to define weights to your widgets inside the outermost layout. Declare their height as 0dp and then define android:layout_weight to each one of them . Total weigh sum of the three of them should be 1. According to your need you can deine 0.1 weight to both top and bottom TextView's and define 0.8 to ListView.

<TextView
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight = "0.1"
    android:textSize="30sp"
    android:text="Top TextView" />

<ListView
    android:id="@+id/listView_Species"
    android:layout_width="match_parent"
    android:layout_weight = "0.8"
    android:layout_height="0dp" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:textSize="30sp"
    android:layout_weight = "0.1"
    android:text="Bottom TextView" />