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

Dou*_*ugW 347 android android-listview android-scrollview

我一直在寻找这个问题的解决方案,我能找到的唯一答案似乎是" 不要将ListView放入ScrollView ".我还没有看到任何真正的解释为什么.我似乎找到的唯一原因是Google认为你不应该这样做.好吧,我做了,所以我做到了.

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

Dou*_*ugW 260

这是我的解决方案.我是Android平台的新手,我确信这有点hackish,特别是关于直接调用.measure并直接设置LayoutParams.height属性的部分,但它确实有效.

您所要做的就是打电话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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 除了`LinearLayout`没有`ListView`的所有固有细节 - 它没有分隔符,页眉/页脚视图,列表选择器匹配手机的UI主题颜色等.老实说,没有理由为什么你不能滚动内部容器,直到它到达结束,然后让它停止拦截触摸事件,以便外部容器滚动.当您使用滚轮时,每个桌面浏览器都会执行此操作.如果您通过轨迹球导航,Android本身可以处理嵌套滚动,那么为什么不通过触摸? (82认同)
  • 你刚刚重新创建了一个非常昂贵的LinearLayout :) (66认同)
  • 如果listItem是ViewGroup实例,`listItem.measure(0,0)`将抛出NPE.我在`listItem.measure`之前添加了以下内容:`if(listItem instanceof ViewGroup)listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));` (29认同)
  • 不幸的是,当`ListView`可以包含可变高度的项时,这种方法有点缺陷.对`getMeasuredHeight()`的调用只返回目标最小高度而不是实际高度.我尝试使用`getHeight()`代替,但这会返回0.有没有人对如何处理这个有任何见解? (8认同)
  • 使用0以外的填充修复ListView:`int totalHeight = listView.getPaddingTop()+ listView.getPaddingBottom();` (4认同)
  • 对于DoughW的解决方案,有一个错误,但我修复了它,请参阅[我的帖子](http://nex-otaku-en.blogspot.com/2010/12/android-put-listview-in-scrollview.html) . (2认同)
  • 为了从上面回答我自己的问题,我最终通过将调用更改为"measure(0,0)"来修复此问题.相反,这个方法应该通过`MeasureSpec.makeMeasureSpec()`传递已知的宽度和未指定的高度.对于宽度,指定测量模式`MeasureSpec.AT_MOST`,对于高度,指定`MeasureSpec.UNSPECIFIED`.将这两个调用的结果传递给`listItem.measure()`. (2认同)

Rom*_*Guy 195

使用a ListView使其不滚动是非常昂贵的,并且违背了整个目的ListView.你应该这样做.只需使用一个LinearLayout.

  • 这不是一个答案"如何在没有崩溃的情况下将ListView放入ScrollView中?"......你可以告诉你不应该这样做,但也要回答如何做到这一点,这是一个很好的答案.你知道,除了滚动之外,ListView还有其他很酷的功能,而LinearLayout没有这些功能. (125认同)
  • 因为我在过去的2到3年里一直负责ListView,所以来源就是我:) ListView做了很多额外的工作来优化适配器的使用.试图解决它仍然会导致ListView做很多工作,而LinearLayout不必这样做.我不会详细介绍,因为这里没有足够的空间来解释ListView的实现.这也无法解决ListView相对于触摸事件的行为方式.如果您的黑客今天仍在使用,它仍然无法保证将来的版本仍可使用.使用LinearLayout真的不会有太多工作. (69认同)
  • 如果您有一个包含ListView +其他视图的区域,并且您希望所有内容都滚动为一个怎么办?这似乎是将ListView放在ScrollView中的合理用例.用LinearLayout替换ListView不是一个解决方案,因为那时你不能使用适配器. (44认同)
  • 那是一个合理的回应.如果我可以分享一些反馈,我会有更多重要的iPhone体验,我可以告诉你,Apple的文档在性能特征和用例(或反模式)方面写的要好得多.总体而言,Android文档分布更广,重点更少.我知道背后有一些原因,但这是一个长时间的讨论,所以如果你觉得有必要聊聊它,请告诉我. (7认同)
  • 您可以轻松编写一个静态方法,从适配器创建LinearLayout. (6认同)
  • 这对我来说几次令人沮丧.它是这样的:1,你用listview构建一个列表,认为这就是你所需要的.2:之后设计添加了按钮/文本字段/列表上方或下方的任何内容.Listview不允许这样做.3:你必须抛弃listview实现并重新开始.结论:永远不要使用listview,除非你绝对肯定你在设计中所需要的一切. (4认同)
  • @Karl ListView允许这样做.如果您希望按钮/文本字段/可以使用列表滚动,则可以使用页眉/页脚. (3认同)
  • 我不确定对于Horizo​​ntalScrollView中的ListView也是如此,反之亦然.听起来像是一个完全合法的用例吗? (2认同)
  • 是否有基于适配器的等效不可滚动列表?LinearLayout不使用适配器.我正在尝试重用现有的列表适配器. (2认同)
  • @RomainGuy:嗨,你怎么说'只需使用LinearLayout'?你的意思是实际为每个数据项创建一个`LinearLayout`吗? (2认同)
  • 这是使用linearlayout进行DIY的真正好处 - 如果您使用`<include />`标签进行线性布局并包含几个相同的布局,那么您的子视图中将不会有唯一的ID,如果您的活动将丢失任何视图状态被重新创造.这可能是listview + adapter正常处理的事情. (2认同)

Atu*_*waj 89

这肯定会工作............
你只需更换你的<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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是非常好的.它还可以将一个谷歌地图片段放在ScrollView中,它仍然可以放大/缩小.谢谢. (4认同)

小智 25

把它放在ListView里面ScrollView,我们可以ListView用作ScrollView.必须进入的东西ListView可以放在里面ListView.ListView可以通过向页眉和页脚添加布局来放置顶部和底部的其他布局ListView.所以整个ListView会给你一个滚动的体验.

  • 这是最优雅和恰当的答案imho.有关此方法的更多详细信息,请参阅以下答案:http://stackoverflow.com/a/20475821/1221537 (3认同)

dju*_*nod 21

很多情况下,在ScrollView中使用ListView非常有意义.

这里的代码基于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();
}
Run Code Online (Sandbox Code Playgroud)

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


Jas*_*n Y 17

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

将heightMeasureSpec操作为具有非常大的AT_MOST(Integer.MAX_VALUE >> 4)会导致ListView测量其所有子节点到达给定(非常大)的高度并相应地设置其高度.

由于以下几个原因,这比其他解决方案效果更好:

  1. 它正确测量一切(填充,分隔)
  2. 它在度量过程中测量ListView
  3. 由于#2,它无需任何额外代码即可正确处理项目宽度或数量的变化

在缺点方面,您可能会认为这样做依赖于SDK中可能发生变化的无证行为.另一方面,您可能会认为这就是wrap_content应该如何与ListView一起使用,并且当前的wrap_content行为刚刚被破坏.

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

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


Tal*_*tle 13

有一个内置的设置.在ScrollView上:

android:fillViewport="true"
Run Code Online (Sandbox Code Playgroud)

在Java中

mScrollView.setFillViewport(true);
Run Code Online (Sandbox Code Playgroud)

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

  • 这适用于他演示的LinearLayout.它不适用于ListView.根据该帖子的日期,我想他写的是为了提供他的另一个例子,但这并没有回答这个问题. (2认同)

Ash*_*ini 8

我们不能使用两个滚动simulteniuosly.我们将获得ListView的总长度并使用总高度扩展listview.然后我们可以直接在ScrollView中添加ListView或使用LinearLayout,因为ScrollView直接有一个子节点.在代码中复制setListViewHeightBasedOnChildren(lv)方法并展开listview,然后可以在scrollview中使用listview.\ 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>
Run Code Online (Sandbox Code Playgroud)

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);
      }
Run Code Online (Sandbox Code Playgroud)


Ded*_*mar 8

您创建不可滚动的自定义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();    
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的布局资源文件中

<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>
Run Code Online (Sandbox Code Playgroud)

在Java文件中

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


小智 6

这是唯一对我有用的东西:

在棒棒糖上你可以使用

yourtListView.setNestedScrollingEnabled(true);
Run Code Online (Sandbox Code Playgroud)

如果您需要向后兼容旧版本的操作系统,这将启用或禁用此视图的嵌套滚动,您将不得不使用 RecyclerView。