How can I put a ListView into a ScrollView without it collapsing?

后端 未结 27 3220
轮回少年
轮回少年 2020-11-21 05:24

I\'ve searched around for solutions to this problem, and the only answer I can find seems to be \"don\'t put a ListView into a ScrollView\". I have yet to see any real expl

相关标签:
27条回答
  • 2020-11-21 05:42

    There are plenty of situations where it makes a lot of sense to have ListView's in a ScrollView.

    Here's code based on DougW's suggestion... works in a fragment, takes less memory.

    public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            return;
        }
        int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
        int totalHeight = 0;
        View view = null;
        for (int i = 0; i < listAdapter.getCount(); i++) {
            view = listAdapter.getView(i, view, listView);
            if (i == 0) {
                view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, LayoutParams.WRAP_CONTENT));
            }
            view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
            totalHeight += view.getMeasuredHeight();
        }
        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
        listView.requestLayout();
    }
    

    call setListViewHeightBasedOnChildren(listview) on each embedded listview.

    0 讨论(0)
  • 2020-11-21 05:42

    ListView is actually already capable of measuring itself to be tall enough to display all items, but it doesn't do this when you simply specify wrap_content (MeasureSpec.UNSPECIFIED). It will do this when given a height with MeasureSpec.AT_MOST. With this knowledge, you can create a very simple subclass to solve this problem which works far better than any of the solutions posted above. You should still use wrap_content with this subclass.

    public class ListViewForEmbeddingInScrollView extends ListView {
        public ListViewForEmbeddingInScrollView(Context context) {
            super(context);
        }
    
        public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 4, MeasureSpec.AT_MOST));
        }
    }
    

    Manipulating the heightMeasureSpec to be AT_MOST with a very large size (Integer.MAX_VALUE >> 4) causes the ListView to measure all of its children up to the given (very large) height and set its height accordingly.

    This works better than the other solutions for a few reasons:

    1. It measures everything correctly (padding, dividers)
    2. It measures the ListView during the measure pass
    3. Due to #2, it handles changes in width or number of items correctly without any additional code

    On the downside, you could argue that doing this is relying on undocumented behavior in the SDK which could change. On the other hand, you could argue that this is how wrap_content should really work with ListView and that the current wrap_content behavior is just broken.

    If you're worried that the behavior could change in the future, you should simply copy the onMeasure function and related functions out of ListView.java and into your own subclass, then make the AT_MOST path through onMeasure run for UNSPECIFIED as well.

    By the way, I believe that this is a perfectly valid approach when you are working with small numbers of list items. It may be inefficient when compared to LinearLayout, but when the number of items is small, using LinearLayout is unnecessary optimization and therefore unnecessary complexity.

    0 讨论(0)
  • 2020-11-21 05:42

    A solution I use is, to add all Content of the ScrollView (what should be above and under the listView) as headerView and footerView in the ListView.

    So it works like, also the convertview is resued how it should be.

    0 讨论(0)
  • 2020-11-21 05:42

    This library is the easiest and quickest solution to the problem.

    0 讨论(0)
  • 2020-11-21 05:43

    Here's my solution. I'm fairly new to the Android platform, and I'm sure this is a bit hackish, especially in the part about calling .measure directly, and setting the LayoutParams.height property directly, but it works.

    All you have to do is call Utility.setListViewHeightBasedOnChildren(yourListView) and it will be resized to exactly accommodate the height of its items.

    public class Utility {
        public static void setListViewHeightBasedOnChildren(ListView listView) {
            ListAdapter listAdapter = listView.getAdapter();
            if (listAdapter == null) {
                // pre-condition
                return;
            }
    
            int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
    
            for (int i = 0; i < listAdapter.getCount(); i++) {
                View listItem = listAdapter.getView(i, null, listView);
                if (listItem instanceof ViewGroup) {
                    listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                 }
    
                 listItem.measure(0, 0);
                 totalHeight += listItem.getMeasuredHeight();
            }
    
            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
            listView.setLayoutParams(params);
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:43

    There's a built-in setting for it. On the ScrollView:

    android:fillViewport="true"
    

    In Java,

    mScrollView.setFillViewport(true);
    

    Romain Guy explains it in depth here: http://www.curious-creature.org/2010/08/15/scrollviews-handy-trick/

    0 讨论(0)
提交回复
热议问题