Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Listview Measure Height

I'm trying to find a way to set the height of a listview based on the height of its children items. I followed the solution given here: How can I put a ListView into a ScrollView without it collapsing?

public 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);

        if(listItem != null){
            listItem.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));                
            listItem.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            //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();
}

Now I call the above function on the listview like this:

public void displayReviews(ArrayList<Reviews> resultReviews){
    // Hide the loading progress
    hideReviewsLoading();

    if(resultReviews != null && resultReviews.size() > 0){
        mCurrentReviewList.onFetchFinished(resultReviews);
        setListViewHeightBasedOnChildren(mCurrentReviewList.getListView());
    }
    else{
        // Display a generic text to indicate no reviews are in yet
        displayEmptyText();
    }
}

Here above the mCurrentreviewList is a ListFragment which basically has an adapter to set the elements within a layout.

The problem I'm having is that the height of each listitem that it measures is not accurate. And so in the end when all the list items(reviews) are populated the overall listview that contains it, never fully displays all the list items. It cuts off somewhere below - Like only shows 7.5 reviews out of a 10 total.

I'm not sure what I'm doing incorrectly. Any help and direction would be greatly appreciated!

like image 651
falc0nit3 Avatar asked Nov 28 '22 04:11

falc0nit3


1 Answers

Ok looks like I was able to figure out why the measurement was inaccurate. I was trying to get a measurement of the listview's height by passing

listItem.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

For ListItem's with variable/dynamic height, this will not work out. The measurement has to know at least one variable and in this case it will be the constant width. So, first calculate the desired width of the listview which you know is going to be constant, by using:

int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);

and then pass it to the listitem measure. Full code is given below:

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

    int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);

        if(listItem != null){
            // This next line is needed before you call measure or else you won't get measured height at all. The listitem needs to be drawn first to know the height.
            listItem.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
            listItem.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
            totalHeight += listItem.getMeasuredHeight();

        }
    }

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

Please Note This is a hackish implementation and isn't the recommended way. Please see How can I put a ListView into a ScrollView without it collapsing? and understand exactly what you are doing before implementing this.

like image 137
falc0nit3 Avatar answered Dec 09 '22 16:12

falc0nit3