-1

I have a Listview in which I need to obtain the total height sum of all the items so that the ListView height equals that height of all the items. I tried to implement the following class to accomplish that.

public class UIUtils {

    /**
     * Sets ListView height dynamically based on the height of the items.
     *
     * @param listView to be resized
     * @return true if the listView is successfully resized, false otherwise
     */
    public static boolean setListViewHeightBasedOnItems(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if(listAdapter != null)
        {
            int numberOfItems = listAdapter.getCount();
            WindowManager wm = (WindowManager) listView.getContext().getSystemService(Context.WINDOW_SERVICE);
            Display display = wm.getDefaultDisplay();
            int deviceWidth;

            if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
                Point size = new Point();
                display.getSize(size);
                deviceWidth = size.x;
            } else {
                deviceWidth = display.getWidth();
            }

            int desiredWidth = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
            int desiredHeight = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);

            // Get total height of all items.
            int totalItemsHeight = 0;
            for(int itemPos = 0; itemPos < numberOfItems; itemPos++)
            {
                View item = listAdapter.getView(itemPos, null, listView);
                item.measure(desiredWidth, desiredHeight);
                totalItemsHeight += item.getMeasuredHeight();
            }

            // Get total height of all item dividers.
            int totalDividersHeight = listView.getDividerHeight() * (numberOfItems - 1);
            int totalPadding        = listView.getPaddingBottom() + listView.getPaddingTop();

            // Set list height.
            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalItemsHeight + totalDividersHeight + totalPadding;
            listView.setLayoutParams(params);
            listView.requestLayout();

            return true;
        }
        else
        {
            return false;
        }
    }
}

Here is how I finally make use of the class described before:

ListView list = (ListView) findViewById(R.id.bills);
BillsListViewAdapter listViewAdapter = new BillsListViewAdapter(context, R.layout.item_bill, bills);
list.setAdapter(listViewAdapter);
UIUtils.setListViewHeightBasedOnItems(list);

The mentioned class will only return a static value for all the items even though on some instances the height may vary because the text of a TextView inside the item might have two or more lines.

item_bil.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:background="?android:attr/activatedBackgroundIndicator"
              android:padding="10dp"
              android:weightSum="1">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.9"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="5dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/number"
                android:layout_marginEnd="2dp"
                android:textSize="12sp"/>

            <TextView
                android:id="@+id/number"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="{{number}}"
                android:textSize="12sp"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/hour"
                android:layout_marginStart="@dimen/activity_horizontal_margin"
                android:layout_marginEnd="2dp"
                android:textSize="12sp"/>

            <TextView
                android:id="@+id/hour"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="{{hour}}"
                android:textSize="12sp"/>

        </LinearLayout>

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="{{name}}"
            android:textSize="16sp"
            android:textColor="@color/colorPrimary"
            android:layout_marginBottom="5dp"
            android:gravity="center_vertical"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="5dp">

            <TextView
                android:id="@+id/address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="{{address}}"
                android:textStyle="bold"
                android:textSize="12sp"
                android:gravity="center_vertical"/>

        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="0.1">

        <TextView
            android:id="@+id/amount"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="{{amount}}"
            android:textSize="18sp"
            android:gravity="right"/>

    </LinearLayout>

</LinearLayout>

ListView:

<ListView
    android:id="@+id/charges"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scrollbars="none"/>
Joscplan
  • 1,024
  • 2
  • 15
  • 35
  • Can you explain in detail why are you setting the listview height programmatically and not in your layout file? Using a `RelativeLayout` you can place all your views and give the required space to your listview. If you have only listview in the layout, you can just set its height to `match_parent`. – Prasad Pawar May 16 '17 at 22:01
  • @PrasadPawar That is because I have that ListView inside a CardView which at the same time is inside a ScrollView. – Joscplan May 16 '17 at 22:10
  • So any reason why you are not using a vertical `LinearLayout`? Adding a listview inside scrollview is not recommended by Android. Even if you get past this you'll face new issues. If you want i can post a small snippet to demonstrate what I'm saying. – Prasad Pawar May 16 '17 at 22:15
  • @PrasadPawar Iam using one, sorry I didnt mention that. The actual hierarchy is: ScrollView > LinearLayout (vertical) > CardView > ListView – Joscplan May 16 '17 at 22:29
  • Sorry but the entire structure seems to be poorly planned. From what it looks like you can use. A listview as the root view. Then list items can be your card views. The card view can contain all your views stacked in a vertical linear layout. Again, if you are nesting any scrollable view Inside a scrollview, you are doing something wrong. – Prasad Pawar May 16 '17 at 22:35
  • @PrasadPawar I can see that and Iam aware of that, the the issue here is not that but the problem of not being able to obtain a dynamic "real" height of each of the ListView items. I keep getting a constant height which is incorrect as I mentioned above a TextView inside each Item may contain more than 1 line in some cases so the height of the entire Item should be bigger at some cases. – Joscplan May 16 '17 at 22:39
  • No that's not possible please go through Android documentation for listview. You cannot get height of all items in the adapter. – Prasad Pawar May 16 '17 at 23:08

1 Answers1

0

Try this code. It's working fine in my App.

import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;

 /**
 * Created by Zohaib Hassan on 5/25/2015.
 */
public class ListviewHelper {

public static void getListViewSize(ListView myListView) {
    ListAdapter myListAdapter = myListView.getAdapter();
    if (myListAdapter == null) {
        //do nothing return null
        return;
    }
    //set listAdapter in loop for getting final size
    int totalHeight = 0;
    for (int size = 0; size < myListAdapter.getCount(); size++) {
        View listItem = myListAdapter.getView(size, null, myListView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }
    //setting listview item in adapter
    ViewGroup.LayoutParams params = myListView.getLayoutParams();
    View listItem = myListAdapter.getView(myListAdapter.getCount() - 1, null, myListView);
    listItem.measure(0, 0);
    params.height = totalHeight +  10
            + (myListView.getDividerHeight() * (myListAdapter.getCount() - 1));
    myListView.setLayoutParams(params);
    // print height of adapter on log
    Log.i("height of listItem:", String.valueOf(totalHeight));
}




}

There is another thing that can cause problem, please have a look on that issue as well. Please make sure your Screen is at-least showing one item of your listview completely before scrolling. Otherwise some part of your last item of list will be hidden. If this is the case then you have to add a static value to the total height in "ListViewHelper" class. Let's say:

params.height = totalHeight +  10 + 100
        + (myListView.getDividerHeight() * (myListAdapter.getCount() - 1));

Hope this'll help!

Zohaib Hassan
  • 984
  • 2
  • 7
  • 11
  • I tried it but no luck. One thing I noticed is that listItem.getMeasuredHeight() will always return the same value for each item which is the same issue I had with my original implementation and is incorrect because not all the items are the same height as I mentioned due to the TextView dynamic lines number. – Joscplan May 16 '17 at 22:27
  • Well in this case, in "ListViewHelper" your are getting all the items of list one by one; just get your "TextView" from your "ListViewItem" and calculate it's height; then add the height of each item in "total height". – Zohaib Hassan May 17 '17 at 20:35
  • You can calculate the height of text view in this way: http://stackoverflow.com/questions/24359538/how-to-find-the-text-areaheight-width-of-textview-programmatically-in-android – Zohaib Hassan May 17 '17 at 20:35