Calculate the size of a list view or how to tell it to fully expand

I am currently trying to use a ListView inside of a ScrollView. I know from what I’ve read that this is looked down upon, but I’m trying to get the ListView to expand completely by showing all of its rows so there is no need for it to scroll. I’ve been struggling, however, with how to tell the ListView to completely expand to show all of its rows since it needs a defined height. Does anyone know of a way to calculate the height of a fully expanded ListView before it is drawn?

This problem mainly stems from the fact that you can’t put a scrollable view inside of another scrollable view. I am okay with the fact that the ListView won’t be able to scroll as long as I can make it expand to show all of its rows. I cannot do this, however, without being able to give it a defined height, which it seems I would need to calculate.

  • How can i set up screen lock with a password programmatically?
  • How to emulate a nfc-tag on an android phone
  • Rect and RectF in Android SDK
  • java.lang.IllegalStateException: FirebaseApp with name
  • How to set different themes for inflated layout in a view stub
  • How to read data from XLS (Excel) file
  • See the url below for a sketch (I’m a new user so I’m not allowed to post one). It shows that my full layout is too big for the “physical” screen and needs to scroll in order to show the rest of the list and buttons at the bottom. I’m trying to get across that the “virtual” screen is too big to fit on one screen even without the ListView there.

    http://img51.imageshack.us/img51/7210/screenmockup.png

    Related posts:

    Android App development error: “Bad XML block: header size 60 or total size 3932356 is lar...
    How to create a UI like the new market or Google plus?
    Android: How to release resources when the application terminates?
    Is there any graphical representation of Android views lifecycle (a state diagram)?
    What is the use of BaseColumns in Android
    Java MTP library that works on Windows
  • Blurry SVG images in chrome for Android
  • Get Updated Contacts in Android for Contact Sync
  • How to build an android app with external libraries using ant?
  • Animated splash screen with Phonegap
  • How to use local aar dependency?
  • persian/arabic search in sqlite android gives bad result
  • 12 Solutions collect form web for “Calculate the size of a list view or how to tell it to fully expand”

    Well, thanks to Rudy, his suggestions was very helpful. Here is how it can be implemented.

    1) Create a new class that extends ListView:

    package com.example.android.views;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.widget.ListView;
    
    public class ExpandedListView extends ListView {
    
        private android.view.ViewGroup.LayoutParams params;
        private int old_count = 0;
    
        public ExpandedListView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (getCount() != old_count) {
                old_count = getCount();
                params = getLayoutParams();
                params.height = getCount() * (old_count > 0 ? getChildAt(0).getHeight() : 0);
                setLayoutParams(params);
            }
    
            super.onDraw(canvas);
        }
    
    }
    

    2) … and finally add the new view to your xml layout file:

    <com.example.android.views.ExpandedListView
        android:id="@+id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scrollbars="none"
        android:padding="0px"
        />
    

    The correct way to calculate the height is precisely:

    int height = 0;
    for (int i = 0; i < listView.getChildCount(); i++) {
        height += listView.getChildAt(i).getMeasuredHeight();
        height += listView.getDividerHeight();
    }
    

    Attention!
    Correct way to calculate height is not

    params.height = getCount() * (old_count > 0 ? getChildAt(0).getHeight() : 0);
    

    We have to add height of each (minus one) divider heights.

    if(oldCount > 0 && getCount() > 0)
        params.height = getCount()
                      * (getChildAt(0).getHeight() + getDividerHeight())
                      - getDividerHeight();
    else
        params.height = 0;
    

    If you want a ListView which won’t scroll couldn’t you just use a LinearLayout?

    @Override
    public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        if (this.isExpanded) {
            final int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
            final ViewGroup.LayoutParams params = this.getLayoutParams();
            params.height = this.getMeasuredHeight();
        } else {
           super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
    

    You should not place your ListView in a ScrollView. The ListView is already scrollable.

    The ListView should expand fully by default, if it is the only thing in the layout.

    If it is not the only thing in the layout, and you want the ListView to expand to take up all available space, set the layout_weight on the ListView to 1, where all other layout_weight=0.

    <LinearLayout android:layout_width="fill_parent" 
                 android:layout_height="fill_parent" orientation="vertical">
      <TextView id="@+id/title" 
                 android:layout_width="wrap_content" 
                android:layout_height="wrap_content"/>
      <ListView id="@+id/listView" 
                android:layout_width="fill_parent" 
                android:layout_height="0dp" 
                android:layout_weight="1"/>
    </LinearLayout>
    

    Edit:
    The ListView is really designed to be scrollable…the screen layout you have in your screenshot doesn’t really seem like the “android way”.

    However, if your really want to circumvent that, you could try inflating one of the rows, get its minHeight, and multiply that by the number of items in the ListView adapter.

    LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.layout.label_value_row, null, false);
    int height = adapter.getCount() * view.getMinHeight();
    

    I took the function from djunod’s answer (the function in static class)

    calculate listview size

    and change ondraw method as follow.
    it works after delete operations successfully.

    here is my final class

    class ExpandedListView extends ListView {

    private android.view.ViewGroup.LayoutParams params;
    private int old_count = 0;
    
    public ExpandedListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
    
        if (getCount() != old_count) {
            params = getLayoutParams();
            old_count = getCount();
            int totalHeight = 0;
            for (int i = 0; i < getCount(); i++) {
                this.measure(0, 0);
                totalHeight += getMeasuredHeight();
            }
    
            params = getLayoutParams();
            params.height = totalHeight + (getDividerHeight() * (getCount() - 1));
            setLayoutParams(params);
        }
    
        super.onDraw(canvas);
    }
    

    }

    now my listview expands after row insert without scrolling.
    and after row delete this code calculates the correct size for listview.
    i don’t know how it is working.
    but it is working.
    BR

    I’ve been researching on how to do this, and although the question has already been answered, I’d like to provide a solution that I think is better:

    Looking at the ListView’s source code you can see the following inside the onMeasure(…) code:

    if (heightMode == MeasureSpec.AT_MOST) {
         // TODO: after first layout we should maybe start at the first visible position, not 0
         heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
    }
    

    So simply call the listView’s measure and pass MeasureSpec.AT_MOST for the height instead of the usual MeasureSpec.UNSPECIFIED.

    I do the following to measure a listview and place it inside a popup-window:

        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels, MeasureSpec.EXACTLY);
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels, MeasureSpec.AT_MOST);
        mCatalogView.measure(widthMeasureSpec, heightMeasureSpec);
    
        mPopup.setWidth(mCatalogView.getMeasuredWidth());
        mPopup.setHeight(mCatalogView.getMeasuredHeight());
    

    I ended up overriding onDraw() of the ListView and calculating the average height of the visible rows in the view then estimating the actual height of the list based on how many items are in the adapter. This estimating happens a couple of times until all of the rows are visible.

    I know it’s late and there is already and answer but if you really want to do this there is an easy way:

    Create the list view Item with a defined height

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="horizontal" >
    
        <ImageView
            android:id="@+id/lvItemSurpriseMeImgRestaurant"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_marginLeft="5dp"
            android:contentDescription="@string/restaurantImageDescriptionColon" />
    </LinearLayout>
    

    Now we know that the Item is 100dp high, and if we know the number of items then it is very simple

    set the ListView height literally to the value of NoOfItems*Height + 10dp extra

    It will never change since all unites are in dp.

    If you have more than 1 item type then calculate it with basic math and set it

            <ListView
                android:id="@+id/menuListView"
                android:layout_width="match_parent"
                android:layout_height="210dp"
                android:layout_weight="1.0"
                tools:ignore="NestedScrolling"
                tools:listitem="@layout/menu_item" >
            </ListView>
    

    If you have items with different height you need to use this method:

    public void setListViewHeightBasedOnChildren(ListView listView) {
    
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter != null) {
    
            int numberOfItems = listAdapter.getCount();
    
            // Get total height of all items.
            int totalItemsHeight = 0;
            for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
                View item = listAdapter.getView(itemPos, null, listView);
                float px = 300 * (listView.getResources().getDisplayMetrics().density);
                item.measure(
                        View.MeasureSpec.makeMeasureSpec((int)px, View.MeasureSpec.AT_MOST),
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                int height = item.getMeasuredHeight();
                totalItemsHeight += height;
            }
    
            // Get total height of all item dividers.
            int totalDividersHeight = listView.getDividerHeight() *
                    (numberOfItems - 1);
    
            // Set list height.
            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalItemsHeight + totalDividersHeight;
            listView.setLayoutParams(params);
            listView.requestLayout();
        }
    }
    

    it work just fine with any ListView with any items

    use this will be worked:

    public class ListViewEx extends ListView {
    
    public ListViewEx(Context context) {
        super(context);
    }
    
    /**
     * @param context
     * @param attrs
     */
    public ListViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    /**
     * 设置不滚动
     */
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return ev.getAction()==MotionEvent.ACTION_MOVE||super.dispatchTouchEvent(ev);
    }
    

    }

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.