Android 如何在不折叠的情况下将 ListView 放入 ScrollView?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3495890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 10:27:57  来源:igfitidea点击:

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

androidandroid-listviewandroid-scrollview

提问by DougW

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 explanation for whythough. The only reason I can seem to find is that Google doesn't think you should want to do that. Well I do, so I did.

我四处寻找解决此问题的方法,我能找到的唯一答案似乎是“不要将 ListView 放入 ScrollView”。我还没有看到任何真正的解释,为什么虽然。我似乎能找到的唯一原因是 Google 认为您不应该这样做。好吧,我这样做了。

So the question is, how can you place a ListView into a ScrollView without it collapsing to its minimum height?

所以问题是,如何将 ListView 放入 ScrollView 而不折叠到其最小高度?

采纳答案by Romain Guy

Using a ListViewto make it not scroll is extremely expensive and goes against the whole purpose of ListView. You should NOTdo this. Just use a LinearLayoutinstead.

使用 aListView使其不滚动是非常昂贵的,并且违背了ListView. 你应该这样做。只需使用 aLinearLayout代替。

回答by DougW

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.heightproperty directly, but it works.

这是我的解决方案。我对 Android 平台相当陌生,我确定这有点 hackish,尤其是在关于直接调用 .measure 和直接设置LayoutParams.height属性的部分,但它有效。

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

您所要做的就是调用Utility.setListViewHeightBasedOnChildren(yourListView),它将调整大小以完全适应其项目的高度。

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

回答by Atul Bhardwaj

This will definitely work............
You have to just replace your <ScrollView ></ScrollView>in layout XML file with this Custom ScrollViewlike <com.tmd.utils.VerticalScrollview > </com.tmd.utils.VerticalScrollview >

这肯定会工作............
你只需更换您的<ScrollView ></ScrollView>布局XML文件与此Custom ScrollView类似 <com.tmd.utils.VerticalScrollview > </com.tmd.utils.VerticalScrollview >

package com.tmd.utils;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;

public class VerticalScrollview extends ScrollView{

    public VerticalScrollview(Context context) {
        super(context);
    }

     public VerticalScrollview(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: DOWN super false" );
                    super.onTouchEvent(ev);
                    break;

            case MotionEvent.ACTION_MOVE:
                    return false; // redirect MotionEvents to ourself

            case MotionEvent.ACTION_CANCEL:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: CANCEL super false" );
                    super.onTouchEvent(ev);
                    break;

            case MotionEvent.ACTION_UP:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: UP super false" );
                    return false;

            default: Log.i("VerticalScrollview", "onInterceptTouchEvent: " + action ); break;
        }

        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        super.onTouchEvent(ev);
        Log.i("VerticalScrollview", "onTouchEvent. action: " + ev.getAction() );
         return true;
    }
}

回答by Arun

Insted of putting ListViewinside a ScrollView, we can use ListViewas a ScrollView. Things which has to be in ListViewcan be put inside the ListView. Other layouts on top and bottom of ListViewcan be put by adding layouts to header and footer of ListView. So the entire ListViewwill give you an experience of scrolling .

Insted 放入ListViewa 中ScrollView,我们可以将其ListView用作 a ScrollView。必须在ListView里面的东西可以放在里面ListView。顶部和底部的其他布局ListView可以通过将布局添加到 的页眉和页脚来放置ListView。所以整个ListView会给你一个滚动的体验。

回答by djunod

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

很多情况下,在 ScrollView 中包含 ListView 是很有意义的。

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

这是基于 DougW 建议的代码......在片段中工作,占用更少的内存。

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.

在每个嵌入的列表视图上调用 setListViewHeightBasedOnChildren(listview)。

回答by Jason Y

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.

ListView 实际上已经能够测量自身高度以显示所有项目,但是当您简单地指定 wrap_content (MeasureSpec.UNSPECIFIED) 时它不会这样做。当使用 MeasureSpec.AT_MOST 给定高度时,它将执行此操作。有了这些知识,您可以创建一个非常简单的子类来解决这个问题,它比上面发布的任何解决方案都要好得多。您仍然应该对这个子类使用 wrap_content。

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.

将 heightMeasureSpec 操作为具有非常大尺寸 (Integer.MAX_VALUE >> 4) 的 AT_MOST 会导致 ListView 测量其所有子项直至给定(非常大)高度并相应地设置其高度。

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
  1. 它正确测量所有内容(填充,分隔符)
  2. 它在测量传递期间测量 ListView
  3. 由于 #2,它无需任何额外代码即可正确处理宽度或项目数量的变化

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.

不利的一面是,您可能会争辩说,这样做依赖于 SDK 中可能会发生变化的未记录行为。另一方面,您可能会争辩说这就是 wrap_content 应该如何真正与 ListView 一起工作,并且当前的 wrap_content 行为被破坏了。

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.

如果您担心行为将来会发生变化,您应该简单地将 onMeasure 函数和相关函数从 ListView.java 复制到您自己的子类中,然后通过 onMeasure 为 UNSPECIFIED 运行 AT_MOST 路径。

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.

顺便说一下,当您处理少量列表项时,我相信这是一种非常有效的方法。与 LinearLayout 相比,它可能效率低下,但是当项目数量很少时,使用 LinearLayout 是不必要的优化,因此也没有必要的复杂性。

回答by TalkLittle

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/

Romain Guy 在这里进行了深入解释:http: //www.curious-creature.org/2010/08/15/scrollviews-handy-trick/

回答by Dedaniya HirenKumar

You Create Custom ListView Which is non Scrollable

您创建不可滚动的自定义 ListView

public class NonScrollListView extends ListView {

    public NonScrollListView(Context context) {
        super(context);
    }
    public NonScrollListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                    Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();    
    }
}

In Your Layout Resources File

在您的布局资源文件中

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <!-- com.Example Changed with your Package name -->

    <com.Example.NonScrollListView
        android:id="@+id/lv_nonscroll_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </com.Example.NonScrollListView>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/lv_nonscroll_list" >

        <!-- Your another layout in scroll view -->

    </RelativeLayout>
</RelativeLayout>

In Java File

在 Java 文件中

Create a object of your customListview instead of ListView like : NonScrollListView non_scroll_list = (NonScrollListView) findViewById(R.id.lv_nonscroll_list);

创建您的 customListview 而不是 ListView 的对象,如: NonScrollListView non_scroll_list = (NonScrollListView) findViewById(R.id.lv_nonscroll_list);

回答by Ashish Saini

We could not use two scrolling simulteniuosly.We will have get total length of ListView and expand listview with the total height .Then we can add ListView in ScrollView directly or using LinearLayout because ScrollView have directly one child . copy setListViewHeightBasedOnChildren(lv) method in your code and expand listview then you can use listview inside scrollview. \layout xml file

我们不能同时使用两个滚动。我们将获得 ListView 的总长度并使用总高度展开 listview。然后我们可以直接在 ScrollView 中添加 ListView 或使用 LinearLayout 因为 ScrollView 直接有一个孩子。在您的代码中复制 setListViewHeightBasedOnChildren(lv) 方法并展开列表视图,然后您可以在滚动视图中使用列表视图。\layout xml 文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
 <ScrollView

        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
         android:background="#1D1D1D"
        android:orientation="vertical"
        android:scrollbars="none" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#1D1D1D"
            android:orientation="vertical" >

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#333"
                android:gravity="center_vertical"
                android:paddingLeft="8dip"
                android:text="First ListView"
                android:textColor="#C7C7C7"
                android:textSize="20sp" />

            <ListView
                android:id="@+id/first_listview"
                android:layout_width="260dp"
                android:layout_height="wrap_content"
                android:divider="#00000000"
               android:listSelector="#ff0000"
                android:scrollbars="none" />

               <TextView
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#333"
                android:gravity="center_vertical"
                android:paddingLeft="8dip"
                android:text="Second ListView"
                android:textColor="#C7C7C7"
                android:textSize="20sp" />

            <ListView
                android:id="@+id/secondList"
                android:layout_width="260dp"
                android:layout_height="wrap_content"
                android:divider="#00000000"
                android:listSelector="#ffcc00"
                android:scrollbars="none" />
  </LinearLayout>
  </ScrollView>

   </LinearLayout>

onCreate method in Activity class:

Activity 类中的 onCreate 方法:

 import java.util.ArrayList;
  import android.app.Activity;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
  import android.widget.ListView;

   public class MainActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.listview_inside_scrollview);
    ListView list_first=(ListView) findViewById(R.id.first_listview);
    ListView list_second=(ListView) findViewById(R.id.secondList);
    ArrayList<String> list=new ArrayList<String>();
    for(int x=0;x<30;x++)
    {
        list.add("Item "+x);
    }

       ArrayAdapter<String> adapter=new ArrayAdapter<String>(getApplicationContext(), 
          android.R.layout.simple_list_item_1,list);               
      list_first.setAdapter(adapter);

     setListViewHeightBasedOnChildren(list_first);

      list_second.setAdapter(adapter);

    setListViewHeightBasedOnChildren(list_second);
   }



   public static 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);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }

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

回答by Abandoned Cart

This is a combination of the answers by DougW, Good Guy Greg, and Paul. I found it was all needed when trying to use this with a custom listview adapter and non-standard list items otherwise the listview crashed the application (also crashed with the answer by Nex):

这是 DougW、Good Guy Greg 和 Paul 的答案的组合。我发现当尝试将它与自定义列表视图适配器和非标准列表项一起使用时,这一切都是必需的,否则列表视图会使应用程序崩溃(也因 Nex 的回答而崩溃):

public void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            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);
    }