Cristian
Cristian

Reputation: 200120

How to configure ListView to automatically change its height?

I have three ListView widgets in the same LinearLayout. Something like this (I'm omitting XML elements that are not relevant in this example):

<LinearLayout>
    <ListView
      android:layout_width="fill_parent"
      android:layout_height="360dp"/>
    <ListView
      android:layout_width="fill_parent"
      android:layout_height="360dp"/>
    <ListView
      android:layout_width="fill_parent"
      android:layout_height="360dp"/>
</LinearLayout>

This forces the list to have a height of 360 dip. Of course, that will be its height even if there are few list items. So, my question is how can make the lists have an automatic height? What I want is that the ListView height takes the exact size of the sum of all its list items.

Upvotes: 5

Views: 9654

Answers (5)

Thamays
Thamays

Reputation: 3098

try this... list view real height

list view xml coding

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

list view java coding

    ListView lView = (ListView)findViewById(R.id.my_listview);
    lView.setAdapter(adapter);
    setListViewHeightBasedOnChildren(lView);

setListViewHeightBasedOnChildren method

public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        return;
    }

    int totalHeight = 0;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

Upvotes: 2

vuhung3990
vuhung3990

Reputation: 6829

i found a solution on set ListView Height Based On Children, this work

public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter(); 
    if (listAdapter == null) {
        // pre-condition
        return;
    }

    int totalHeight = 0;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

Upvotes: 3

durilka
durilka

Reputation: 1449

Instead of editing my previous answer, here's my actual version (after finally the case became the issue and I had to fix it)

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0));

    int childHeight = getMeasuredHeight() - (getListPaddingTop() + getListPaddingBottom() +  getVerticalFadingEdgeLength() * 2);

    // on a first run let's have a space for at least one child so it'll trigger remeasurement
    int fullHeight = getListPaddingTop() + getListPaddingBottom() + childHeight*(getCount());

    int newChildHeight = 0;
    for (int x = 0; x<getChildCount(); x++ ){
        View childAt = getChildAt(x);

        if (childAt != null) {
            int height = childAt.getHeight();
            newChildHeight += height;
        }
    }

    //on a second run with actual items - use proper size
    if (newChildHeight != 0)
        fullHeight = getListPaddingTop() + getListPaddingBottom() + newChildHeight;

    setMeasuredDimension(getMeasuredWidth(), fullHeight);
}

The trickiest part is that the onMeasure gets called twice, first for approx layout and then after items added for a precise one and both have to return sane results.

Upvotes: 3

durilka
durilka

Reputation: 1449

I've implemented it this way (code is work in progress so it's more a idea source than solution):

package com.customcontrols;
public class NoScrollListView extends ListView
{
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0) );

        // here I assume that height's being calculated for one-child only, seen it in ListView's source which is actually a bad idea
        int childHeight = getMeasuredHeight() - (getListPaddingTop() + getListPaddingBottom() +  getVerticalFadingEdgeLength() * 2);

        int fullHeight = getListPaddingTop() + getListPaddingBottom() + childHeight*(getCount());

        setMeasuredDimension(getMeasuredWidth(), fullHeight);
    }
}

the calculation's not perfect, but it's close and works so far. after that you just create a layout like this:

ScrollView com.customcontrol.NoScrollListView com.customcontrol.NoScrollListView com.customcontrol.NoScrollListView /ScrollView

The scrollView's crucial since you can easily run out of screen bounds.

PS. The calculation's rectum-driven since most of the calculation methods in ListView&Co are package private which is quite a strange choice for publicly inheritable classes for UI.

Upvotes: 8

CommonsWare
CommonsWare

Reputation: 1006944

You can try iterating over the ListView's rows (see methods on ViewGroup), find their heights, sum them, and change your ListView height.

That being said, I fully expect this to be awful:

  • You need to take into account the screen size, so your lists do not go past the bounds of the screen
  • You need to take into account orientation changes
  • You need to take into account any modifications to your data (e.g., deleting rows may require your list to shrink)
  • Etc.

If your goal really is to have a single list, but you have three sources of data, merge the data and put it into a single list. My MergeAdapter can help here.

Upvotes: 1

Related Questions