wsanville
wsanville

Reputation: 37516

ListView pushes other Views off the screen

I'm struggling to get a layout looking correctly, and I've tried to produce the shortest, smallest possible example of my problem.

My goal is to have a header and footer View, at the top and bottom of the screen, with a ListView in between the two, with another View (let's call it the label, it's the gray box from the screen shots) directly below the ListView. This label, and the footer should always be shown when ListView needs to scroll.

Visual Result

When the ListView does not need to scroll (this is correct):

when the ListView doesn't need to scroll

When the ListView needs to scroll, the footer and the gray box are pushed off screen (wrong):

when the ListView needs to scroll

Layout

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical">
    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="header"
            android:padding="20dp"
            android:textSize="18sp"
            android:background="@color/red"/>
    <ListView android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:id="@android:id/list" />
    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="this should be directly below the ListView, but not pushed off screen when the ListView needs to scroll"
            android:padding="5dp"
            android:background="@color/light_gray"
            android:textColor="@color/black"/>
    <!-- Used to push the footer to the bottom -->
    <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="1"/>
    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="footer"
            android:padding="20dp"
            android:textSize="18sp"
            android:background="@color/blue"/>
</LinearLayout>

Test Activity

public class TestActivity extends ListActivity
{
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        ArrayList<String> items = new ArrayList<String>();
        items.add("one");
        items.add("two");
        items.add("three");
        items.add("four");
        items.add("five");
        items.add("six");
        items.add("seven");
        items.add("eight");
        items.add("nine");
        items.add("ten");
        setListAdapter(new ArrayAdapter<String>(this, R.layout.simple_list_item_1, items));
        setContentView(com.myproject.android.R.layout.test);
    }
}

I've tried a few different approaches, such as giving the ListView layout_weight="1" and removing the blank View that I use to push the footer to the bottom. This is almost what I want, it keeps the footer and label visible when the ListView scrolls, but when it only has 1 or 2 items, I need the gray box right below the ListView. I've also attempted to use a RelativeLayout, without success. I guess I'm completely misunderstanding things.

EDIT

Here's my attempt with a RelativeLayout which still isn't correct.

<?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">
    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="header"
            android:padding="20dp"
            android:textSize="18sp"
            android:background="@color/red"
            android:id="@+id/header"
            />
    <ListView android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:id="@android:id/list"
              android:layout_below="@id/header"/>
    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="this should be directly below the ListView, but not pushed off screen when the ListView needs to scroll"
            android:padding="5dp"
            android:background="@color/light_gray"
            android:textColor="@color/black"
            android:layout_below="@android:id/list"/>

    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="footer"
            android:padding="20dp"
            android:textSize="18sp"
            android:background="@color/blue"
            android:layout_alignParentBottom="true"
            android:id="@+id/footer"/>
</RelativeLayout>

Relative Layout (Still Wrong):

relative layout attempt

Upvotes: 10

Views: 9878

Answers (5)

Jenny Pushkarskaya
Jenny Pushkarskaya

Reputation: 21

A solution that worked for me was to add positive padding to the bottom of the list view and negative padding to the top of the "footer". This will work in a linear layout or a relative layout.

<ListView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="50dp"/>

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="-50dp"/>

Upvotes: 2

Leonardo Raele
Leonardo Raele

Reputation: 2814

Two years late to answer the question, but I will leave my solution so it may help someone with the same problem. I solve this problem using 2 nested LinearLayouts and using layout_weigth. Maybe not the best performatic layout, but it reaches the desired effect.

You need to arrange your layout this way:

  1. Your ListView will have wrap_content height to take only the needed space when not filling the entire screen.
  2. Your ListView will be inside a layout with height using layout_weight so the list will take only the needed space when not filling the entire screen and to take only a limited space of the screen when it have size enouth to push the views out of screen.
  3. The grey box view the should be immediately below the list will have wrap_content height and will be a sinbling of the layout of step 2.
  4. This layout and the grey box will be inside a second layout with wrap_content height so they can stay together.
  5. Now you have a layout with the list and the grey view and the list won't push the other views out of screen if it gets too big; you only need to move the footer view to the bottom of the screen.

    5a. If you are using RelativeLayout as your root layout, you can do as sgallego said and use android:layout_alignParentBottom.

    5b. But if you are using LinearLayout you need to create a third layout with layout_weigth and put inside the layout of step 4 and a empty view also with layout_weigth to fill the empty space.

Here is a commented example.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <!-- Header -->
    <TextView
        android:id="@+id/RecordStudy_StudyLabel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/RecordStudy_StudyLabel"
        android:textSize="@dimen/text_large" />

    <!-- Body -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical" >

        <!--
        This layout encapsules the list and the button that must be immediately
        below the list with a wrap_content height, so the list plus the button
        fills only as much space as they need (if the list is not big enouth to
        fill the entire screen).
        -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <!--
            Layout with varaible size with a list inside.
            Using layout_weight tells android that this layout should not grow
            greater then the screen, but uses only the free space. 
             -->
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="vertical" >

                <!--
                Inside this limited height layout, there is a list with height
                wrap_content so it can grow as much as it needs INSIDE the
                layout (through scrolling).
                -->
                <ListView
                    android:id="@+id/RecordStudy_StudyList"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />

            </LinearLayout>

            <!-- Button immediately below the list -->
            <Button
                android:id="@+id/RecordStudy_AddStudy"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/RecordStudy_AddStudy" />

        </LinearLayout>

        <!-- Space between the list and the footer -->
        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

    </LinearLayout>

    <!-- Footer -->
    <Button
        android:id="@+id/RecordStudy_ConfirmButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/RecordStudy_ConfirmButton" />

</LinearLayout>

Upvotes: 1

Siva
Siva

Reputation: 9101

One solution that I implemented and found useful was to keep the listview inside a linear layout with fixed height so that it doesn't extend and overlap other items.

Something like this:

<TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="header"
            android:padding="20dp"
            android:textSize="18sp"
            android:background="@color/red"
            android:id="@+id/header"
            />

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="150dip" //assume 150dip height is sufficient

    <ListView android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:id="@android:id/list"
              android:layout_below="@id/header"/>
</LinearLayout>

    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="this should be directly below the ListView, but not pushed off screen when the ListView needs to scroll"
            android:padding="5dp"
            android:background="@color/light_gray"
            android:textColor="@color/black"
            android:layout_below="@android:id/list"/>

Upvotes: 0

sgallego
sgallego

Reputation: 318

This layout adds the header a top of the screen and the footter and the bottom. The list fills the rest of the screen. With theses aproach list elements never be obscured by the footer. See how to add the gray box below the XML...

<?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">
<TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="header"
        android:padding="20dp"
        android:textSize="18sp"
        android:background="@color/red"
        android:id="@+id/header"
        />
<TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="footer"
        android:padding="20dp"
        android:textSize="18sp"
        android:background="@color/blue"
        android:layout_alignParentBottom="true"
        android:id="@+id/footer"/>
<ListView android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:id="@android:id/list"
          android:layout_below="@id/header"
          android:layout_above="@id/footer"/>


</RelativeLayout>

Ok. This XML solves the problem of the missing footer. Now we have to add a gray box at the end of the list. I think there is two ways to do it:

Upvotes: 7

dmon
dmon

Reputation: 30168

Add android:layout_weight="1" to the listview. That will make it the biggest element in the LinearLayout, without pushing the other ones off the screen.

Upvotes: 10

Related Questions