93

I want to show a button under a ListView. Problem is, if the ListView gets extended (items added...), the button is pushed out of the screen.

I tried a LinearLayout with weights (as suggested in Android: why is there no maxHeight for a View?), but either I got the weights wrong or it simply didn't work.

Also, I found somewhere the hint to use a RelativeLayout. The ListView would then be set above the button with the android:layout_above param.

Problem with this is that I don't know how to position the button afterwards. In the example I found, the View below the ListView was adjusted using android:layout_alignParentBottom, but I don't want my button to cling to the bottom of the screen.

Any ideas apart from using setHeight-method and some calculating of the required space?


Edit: I got a lot of useful answers.

  • bigstone's & user639183's solution almost worked perfectly. However, I had to add an extra padding/margin to the bottom of the button, as it still would be pushed half way out of the screen (but then stopped)

  • Adinia's answer with the relative layout only is fine if you want the button fixed to the bottom of the screen. It's not what I intended but still might be useful for others.

  • AngeloS's solution was the one I chose at the end as it just created the effects I wanted. However, I made two minor changes to the LinearLayout around the button:

    • First, as I didn't want to have any absolute values in my layout, I changed android:layout_height="45px" to wrap_content, which works just fine as well.

    • Second, as I wanted the button to be centered horizontally, which is only supported by vertical LinearLayout, I changed android:orientation="horizontal" to "vertical".

    AngeloS also stated in his initial post that he was not sure if the android:layout_weight="0.1" param in the LinearLayout around the ListView had any effect; I just tried and it actually does! Without, the button gets pushed out of the screen again.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
jellyfish
  • 7,868
  • 11
  • 37
  • 49
  • this way the inner relative layout is growing longer than the screen, one step could be adding `android:layout_alignParentBottom="true"`. But to be clear, do you want the button to stay attached to the bottom of the ListView when there are few items? If yes, see what Rich says. – bigstones Mar 30 '11 at 15:42
  • yes, that's what I want. – jellyfish Mar 31 '11 at 07:12
  • years after, we now have maximum height thanks to ConstraintLayout: https://stackoverflow.com/questions/42694355/how-to-set-recyclerview-max-height/48970129#48970129 – user1506104 Sep 16 '18 at 12:54

16 Answers16

70

I solved this problem in code, setting height of listview only if it has more than 5 items in it:

if(adapter.getCount() > 5){
        View item = adapter.getView(0, null, listView);
        item.measure(0, 0);         
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, (int) (5.5 * item.getMeasuredHeight()));
        listView.setLayoutParams(params);
}

Notice that I set the max height to 5.5 times the height of a single item, so the user will know for sure there is some scrolling to do! :)

shaylh
  • 1,871
  • 3
  • 17
  • 19
  • 1
    Nice answer. With that code i discovery the height of my item, and now I can set the size of my list to show how many itens I wanna. Thanks. – Derzu Apr 20 '12 at 13:53
  • 3
    This is the best answer. Why write 50 lines of nested XML when you can solve the problem with 5 lines of code? – Greg Ennis Aug 25 '13 at 02:55
  • Even though I cleaned up the XML solutions nicely, I still ended up using this idea in my final product. – Richard Le Mesurier Jun 18 '14 at 11:35
  • Though it's a little late to comment on this code but this is really working in me! Thanks. by the way i Use adapter.getCount instead of 5.5. – JayR Aug 01 '14 at 07:30
  • 1
    @JayR it's never too late to comment – Manza Jan 25 '16 at 16:12
  • Good solution. Also instead of `measure(0, 0)` we can use `measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)` making it more readable. – Tejas Jun 01 '17 at 11:16
55

I had this exact issue and to solve it I created two seperate LinearLayouts to house the ListView and Button respectively. From there I put both in another Linear Layout and set that container's android:orientation to vertical. I also set the weight of the the LinearLayout that housed the ListView to 0.1 but I dont know if that has any effect. From there, you can set the height of the bottom container (that has your button) to whatever height you would like.

EDIT this is what i mean:

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

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"
    android:orientation="horizontal">

    <ListView
        android:id="@+id/ListView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="2px"></ListView>
</LinearLayout>

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="45px"
    android:background="@drawable/drawable"
    android:orientation="horizontal">

    <Button
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/btn_more2"
        android:paddingRight="20px" />

</LinearLayout>

The above solution will fix the button the the bottom of the screen.


To have the button float at the bottom of the list, change the height of ListView01 to wrap_content:

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

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"
    android:orientation="horizontal">

    <ListView
        android:id="@+id/ListView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:dividerHeight="2px"></ListView>
</LinearLayout>

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="45px"
    android:background="@drawable/drawable"
    android:orientation="horizontal">

    <Button
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/btn_more2"
        android:paddingRight="20px" />
</LinearLayout>

Wilmer
  • 482
  • 8
  • 11
AngeloS
  • 5,536
  • 7
  • 40
  • 58
  • I see it as something simliar to `
    – AngeloS Mar 30 '11 at 15:33
  • And with regards to overhead, its not much different then what you are doing with the relative layouts .. right now you have one outter wrapper, and you are nesting another .. im am suggesting you nest just one more and split up the list view and the button into two containers creating a stack effect in the vertical linear layout. I can not stress enough that this is a working solution. – AngeloS Mar 30 '11 at 15:43
  • 1
    YES! this works! important to note, the only difference between your first and 2nd solution is the linearlayout needs to wrap_content, so i ended up with an inception type set up: http://imgur.com/c9L1Z. Toa anyone else that finds this: the weighting is important, but can take any value. – Sam Sep 13 '12 at 23:14
  • 2
    no need for wrapping all the views like that - check out [@Sparkle's solution](http://stackoverflow.com/a/17254622/383414) – Richard Le Mesurier Jun 18 '14 at 11:18
  • Is it needed to put 45px or an exact value for the button layout? I need to put wrap_content instead – Mario Velasco Dec 29 '14 at 19:33
  • From what I understand, height of a ListView should never be wrap_content, as it causes getView() to be called many times to estimate the proper height. If it's set to fill_parent, this is not an issue. – Brandon Sep 18 '15 at 14:54
22

On your last edit, you just have to also add android:layout_above="@+id/butt1" in your ListView code.

And to show you(and everybody here who tried an answer earlier) that you really don't need to use more than one RelativeLayout, here is your corrected code:

   <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bkg">
    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView1"
        android:layout_alignParentTop="true">
    </TextView>
    <TextView
        android:id="@+id/textView2"
        android:layout_below="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView2"
        android:layout_centerHorizontal="true">
    </TextView>
    <Button
        android:id="@+id/butt1"
        android:text="string/string3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true">
    </Button>
    <ListView
        android:id="@+id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dip"
        android:layout_marginBottom="2dip"
        android:drawSelectorOnTop="false"
        android:visibility="visible"
        android:layout_below="@+id/textView2"
        android:layout_above="@+id/butt1" />
    </RelativeLayout>

and the result, using some random list:
enter image description here

Adinia
  • 3,722
  • 5
  • 40
  • 58
  • 1
    Thank you very, very much for your help, but having the button fixed at the bottom of the screen was just *not* what I intended. It's supposed to be at the bottom of the ListView. – jellyfish Mar 31 '11 at 07:41
  • Indeed, seems this time LinearLayouts might be the only solution for what you want; I've tried different combinations with RelativeLayouts, and none really worked :( ... But I'm glad you found your answer. – Adinia Mar 31 '11 at 09:31
7

Using LinearLayout, set the height of your ListView and Button to wrap_content and add a weight=1 to ListView. This should work as you want.
And yes, set the layout_gravity of the Button to bottom

ernazm
  • 9,208
  • 4
  • 44
  • 51
6

Found a way to do this without using nested containers, inspired from AngeloS's solution.

I used a LinearLayout with a vertical orientation, that has a ListView and a button. I set the android:layout_weight="0.1" for the ListView.

It manages to get the button stick to the bottom of the list always. And the button does not get pushed off the screen when the list grows.

<ListView
    android:id="@+id/notes_list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"        
    />

<Button
    android:id="@+id/create_note_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"        
    android:onClick="onCreateButtonClicked"
    android:text="@string/create_notes"
    />
Adinia
  • 3,722
  • 5
  • 40
  • 58
Sparkle
  • 61
  • 1
  • 1
  • 2
    No,just tried it and the button stays at the bottom of the screen, it doesn't stay at the bottom of the list. :( – Adinia Jul 09 '13 at 07:08
  • @Adinia This works perfectly at least on 4.4.3. Nice solution, Sparkle, should have more votes. – Richard Le Mesurier Jun 18 '14 at 11:14
  • Humm..maybe you have some other settings in your layout? I have just tested again on a Nexus 4 with 4.4.3, and the button stays at the bottom of the layout, not of the list, it doesn't stick to the list. – Adinia Jun 18 '14 at 13:17
  • 2
    @Richard Le Mesurier Indeed, with `android:layout_height="wrap_content" ` for the LinearLayout, as in your solution, it is working perfectly, and not only on 4.4.3, but since Sparkle didn't mention it, I tried it with `match_parent` before. – Adinia Jun 18 '14 at 13:58
  • Working on Pie also. +1 – Zubair Younas Mar 12 '20 at 10:00
5

RelativeLayout is a solution, if you don't want the button to stay too close to the bottom you can add margins (padding a button would break it because it uses a 9-patch background, and it sets its own padding).

It would be the same if you used a LinearLayout: the way to achieve what you want is to set the ListView to a height=0 and weight=1, and for the button height=wrap_content and weight=0. (setting both to wrap_content, as user639183 said, would not limit ListView's height).

bigstones
  • 15,087
  • 7
  • 65
  • 82
  • Yes, it will limit the `ListView`'s height – ernazm Mar 30 '11 at 14:37
  • @user639183 just tried, you're right I'm sorry, I was so sure about that. – bigstones Mar 30 '11 at 14:54
  • I changed the weight as suggested (before, I had ListView weight=2 and button weight=1, wonder why this didn't work?) The effect is very interesting: the button now is only pushed half way out of the screen. Actually, I can get it to stay where I want it by adding some "android:layout_marginBottom". However, I might also try Maxims approach and see if that's working, too. – jellyfish Mar 30 '11 at 15:02
  • @jellyfish: I don't know, my certainties were demolished by @user639183. The idea is that the whole height will be shared by the two views. The amount of shares they get is given by their weight, so 1-0 -> everything to the first (2-1 would give 1/3 of space to the second). I thought that setting wrap_content on a scrolling view would make it as high as its content. However.. did you set the outer layout to fill_parent? – bigstones Mar 30 '11 at 15:17
  • It's really weird that a nested RelativeLayout (as suggested by Maxim) doesn't work. I might just post it above, as I've got the feeling I just missed something... – jellyfish Mar 30 '11 at 15:23
2

This is the cleanest way to do it:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
  <ListView
      android:id="@+id/ListView01"
      android:layout_width="fill_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:dividerHeight="2px">
  </ListView>
  <FrameLayout
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:background="@drawable/drawable"
      >
    <Button android:background="@drawable/btn_more2"
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:paddingRight="20px"
        />    
  </LinearLayout>
</LinearLayout>

It is similar to Angelos's solution but you don't need the linearlayout that wraps up the ListView. Also you can use a FrameLayout which is lighter compare to a LinearLayout to wrap up the Button.

2

The idea is outlined in the very good solutions by:

They both seem to work perfectly at least on v4.4.3.

I still had to spend some time writing test code to check it out and confirm each method. Below is the self contained, minimal test code required.


Layout is based on Sparkle's solution, as it contains fewer nested layout. Also has been updated to reflect the current LINT checks.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context="MainActivity" >

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

    <Button
        android:id="@+id/btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Grow List"
        tools:ignore="HardcodedText" />

</LinearLayout>

Activity provides the boilerplate code to test the solution for yourself.

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ListView listview = (ListView)findViewById(R.id.listview);
        final ArrayAdapter<String> adapter = 
                new ArrayAdapter<String>(this,
                                         android.R.layout.simple_list_item_1,
                                         new ArrayList<String>());
        adapter.setNotifyOnChange(true);
        listview.setAdapter(adapter);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                int count = adapter.getCount();
                adapter.add(String.valueOf(count));
                listview.setSelection(count);
            }
        });
    }
}

Feel free to add or improve as this is "community wiki".

Community
  • 1
  • 1
Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
2

in LinearLayout, just add android:layout_weight="1" to you ListView

DawnYu
  • 2,278
  • 1
  • 12
  • 14
2

You can probably make this work using nested containers. It's possible that you might need to wrap the Button in a second (inner) container so that it takes up more space, and just align the Button to the top of the inner container.

Possible something like this:

RelativeLayout

-- ListView

-- RelativeLayout

---- Button

Using the different alignment options on the two containers, you should be able to get this to work.

Rich
  • 36,270
  • 31
  • 115
  • 154
  • I think using a relativeLayout is the best solution, but I really can't see why you'd use nested containers, when you can just use something like `android:layout_marginTop="30dip" ` for the Button, if you want more space between the List and Button, for example. – Adinia Mar 30 '11 at 14:56
  • in the question, the OP said that if he strictly makes the positioning of the button relative to the ListView, it goes off the screen once the ListView grows tall enough. In those kinds of cases you have to get tricky with nested containers – Rich Mar 30 '11 at 15:07
1

Try to use RelativeLayout with button in the bottom. Set the height of the layout as "wrap_content". I haven't tried it. Just idea

Maxim
  • 2,996
  • 17
  • 19
  • I really thought this should work, but it seems it doesn't. The layout fills all the space left, as if height was set to match_parent. Doesn't make sense, though. – jellyfish Mar 30 '11 at 15:09
0

Following is what worked for me...

 if(adapter.getCount() > 3){
                    View item = adapter.getView(0, null, impDateListView);
                    item.measure(0, 0);         
                    LayoutParams params = impDateListView.getLayoutParams();
                    params.width = LayoutParams.MATCH_PARENT;
                    params.height = 3 * item.getMeasuredHeight();
                    impDateListView.setLayoutParams(params);


                }

Note : The params.width =... Needs to be set also...

The above example is when you use TableRow and ListView inside TableRow...

0

wrap content in linear layout

in List View add: android:layout_weight="1"

in your Button set fixed height

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

<ListView
    android:id="@+id/my_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1">
</ListView>

 <Button
    android:text="Button"
    android:layout_width="match_parent"
    android:layout_height="50dp"/>

</LinearLayout>
Toma
  • 1
0

If you want the content below the list view to move down as elements are added to the list, try this solution:

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"/>
-1

Since there's no MaxHeight attribute, your best bet is either to set the height of the ListView to a set value, or to use wrap_content.

Chris Cashwell
  • 22,308
  • 13
  • 63
  • 94
-2

I solved this by putting the ListView inside a ScrollView. Lint didn't like it, but by adding a minHeight and setting the fillViewport to true, I have a nice result.

    <ScrollView 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="1dp"
        android:layout_marginRight="1dp"
        android:minHeight="100dp"
        android:fillViewport="true"
        android:fadeScrollbars="false"
        android:paddingTop="2dp" 
        android:orientation="vertical"
        android:scrollbarAlwaysDrawVerticalTrack="true" >

        <ListView
            android:id="@+id/workoutAElistRoutines"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="fill_vertical"
            android:background="@drawable/dialog_inner_border_tan"
            android:choiceMode="singleChoice"
            android:orientation="vertical"
            android:paddingLeft="3dp"
            android:paddingRight="3dp" >

        </ListView>
    </ScrollView>        
Jon
  • 198
  • 1
  • 2
  • 12
  • I tried this approach, even thou it limitates the size of the listview, it does not let me scroll the listview, which is awkward. Do you know why? – Eduardo Aug 09 '12 at 22:37
  • 8
    NEVER put a ListView inside a ScrollView! – jenzz Nov 20 '12 at 10:19
  • Yes, never put a ListView inside a ScrollView. But with this "hack" it's possible... http://stackoverflow.com/a/14577399/1255990 – Leonardo Cardoso Aug 08 '13 at 03:03