Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - Get ListView item height?

Is there a way to get ListViewItem height in code, when there is no actual items in list?

My ListViewItem layout:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight">
...
</LinearLayout>  

I have tried to get it using Inflater:

View convertView = LayoutInflater.from( this )
    .inflate( R.layout.mail_list_row, null );
int itemHeight = convertView.getHeight();

But it's return 0;

Thanks!

like image 473
Maksym Gontar Avatar asked Jul 29 '10 10:07

Maksym Gontar


4 Answers

Try this, it will work for you.

private static final int UNBOUNDED = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

// To calculate the total height of all items in ListView call with items = adapter.getCount()
public static int getItemHeightofListView(ListView listView, int items) {
    ListAdapter adapter = listView.getAdapter();

    int grossElementHeight = 0;
    for (int i = 0; i < items; i++) {
        View childView = adapter.getView(i, null, listView);
        childView.measure(UNBOUNDED, UNBOUNDED);
        grossElementHeight += childView.getMeasuredHeight();
    }
    return grossElementHeight;
}
like image 92
Ashish Dwivedi Avatar answered Nov 11 '22 05:11

Ashish Dwivedi


optimized version of Dwivedi Ji's code with dividers height and without unnecessary params:

private int calculateHeight(ListView list) {

    int height = 0;

    for (int i = 0; i < list.getCount(); i++) {
        View childView = list.getAdapter().getView(i, null, list);
        childView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        height+= childView.getMeasuredHeight();
    }

    //dividers height
    height += list.getDividerHeight() * list.getCount();

    return height;
}
like image 42
ashakirov Avatar answered Nov 11 '22 07:11

ashakirov


In android a view is assigned Width and Height only when its rendering is complete. So unless you list is rendered atleast once you won't get listItemHeight. Solution to your problem could be that you set some min Height of list Item so that you have atleast something to work with instead of Hard Coding height and width.

like image 5
Amit Chintawar Avatar answered Nov 11 '22 07:11

Amit Chintawar


As hariseldon78 above points out none of these solutions fix the REAL problem which is determining the height of the list item row BEFORE it is rendered. I had the same problem as I wanted to scale some images to the height of my ListView item rows, and did not want to scale them to a fixed value. If the theme caused the text in other parts of my row layout to vary in height, I wanted the height so that in my getView routine of my adapter I could resize the bmap accordingly. I was struggling with the problem that getHeight and all the measured heights report zero until the row has been rendered. FOr me seeing the heights correct later was too late.

My solution is to create an onLayoutChangedListener() the first time through getView and only for row 0. The listener will trigger as soon as getView for the first position (0) completes executing, and at that time the "bottom" parameter will tell you the height of the row. I record this in a custom adapter class variable so it is available as a height parameter without having to fetch the height again.

The listener unregisters itself as part of its execution. This provides the proper height for rows 1-N but not for row zero. For row zero I did something really nasty. I had my listener call getView AGAIN for row 0 after setting another custom adapter class variable to control the recursion. The second time getView(0) runs it will not setup the listener, and will find a valid parameter for height to operate with and all is good.

Code is below - no need to tell me how AWFUL this is - if android didn't have to make it so freaking hard to tell how big the view I am creating is when I am done populating the view's based on the rendering parms for the surface I wouldn't have to do this ugliness but it works. Sorry if the code formatting is awful ...

int mHeight = 0;

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
... usual boiler plate stuff
    // JUST THE FIRST TIME
    if (position == 0 && mHeight == 0) {
        final View ref = convertView;
        convertView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            public void onLayoutChange(View v, int left, int top, int right,
                   int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    ref.removeOnLayoutChangeListener(this);
                    mHeight = bottom;
                    firstTime = false;

                    //  NOW LETS REGET THE FIRST VIEW WITH THE HEIGHT CORRECT
                    int visiblePosition = getListView().getFirstVisiblePosition();
                    View view = getListView().getChildAt(0 - visiblePosition);
                    getListAdapter().getView(0, view, getListView());
                    // RECURSION LOL
            }
        });
    }

    // Configure the view for this row
    ....

    // HOW BIG IS THE VIEW?
    // NOW IF NOT FIRSTTIME (MHEIGHT != 0)
    if (mHeight != 0) {
        // DO OUR IMAGE SETUP HERE CAUSE mHeight is RIGHT!
        Log.d(TAG, "mHeight=" + mHeight);
    }

        return convertView;
}
like image 3
Mike Kogan Avatar answered Nov 11 '22 07:11

Mike Kogan