ChatGPT解决这个技术问题 Extra ChatGPT

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

我四处寻找解决此问题的方法,我能找到的唯一答案似乎是“don't put a ListView into a ScrollView”。不过,我还没有看到任何关于 why 的真正解释。我似乎能找到的唯一原因是谷歌认为你不应该这样做。好吧,我做到了,所以我做到了。

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

因为当设备使用触摸屏时,没有很好的方法来区分两个嵌套的可滚动容器。它适用于使用滚动条滚动容器的传统桌面。
当然有。如果他们触及内部的内部,请滚动它。如果内部太大,那是糟糕的 UI 设计。这并不意味着总的来说这是一个坏主意。
刚刚为我的平板电脑应用偶然发现了这个。虽然在智能手机上似乎并没有太糟糕,但在平板电脑上这很可怕,您可以轻松地决定用户是要滚动外部还是内部 ScrollView。 @DougW:糟糕的设计,我同意。
@Romain - 这是一篇相当老的帖子,所以我想知道这里的问题是否已经得到解决,或者仍然没有在 ScrollView 中使用 ListView 的好方法(或者不涉及手动编码的替代方法ListView 的所有细节都变成了 LinearLayout)?
@Jim:“或者不涉及将 ListView 的所有细节手动编码为 LinearLayout 的替代方案”——将所有内容放在 ListView 中。 ListView 可以有多种行样式,以及不可选择的行。

R
Reaz Murshed

这是我的解决方案。我对 Android 平台相当陌生,我敢肯定这有点骇人听闻,尤其是在有关直接调用 .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);
    }
}

您刚刚重新创建了一个非常昂贵的 LinearLayout :)
除了 LinearLayout 没有 ListView 的所有固有优点 - 它没有分隔线、页眉/页脚视图、与手机 UI 主题颜色匹配的列表选择器等。老实说,没有理由为什么你不能滚动内部容器直到它到达末尾然后让它停止拦截触摸事件以便外部容器滚动。当您使用滚轮时,每个桌面浏览器都会执行此操作。如果您通过轨迹球导航,Android 本身可以处理嵌套滚动,那么为什么不通过触摸呢?
如果 listItem 是 ViewGroup 实例,listItem.measure(0,0) 将引发 NPE。我在 listItem.measure 之前添加了以下内容:if (listItem instanceof ViewGroup) listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
修复了填充不是 0 的 ListView:int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
不幸的是,当 ListView 可以包含可变高度的项目时,这种方法有点缺陷。对 getMeasuredHeight() 的调用仅返回目标最小高度,而不是实际高度。我尝试改用 getHeight(),但这会返回 0。有人知道如何解决这个问题吗?
M
Muhammad Babar

使用 ListView 使其不滚动非常昂贵,并且违背了 ListView 的全部目的。您应该这样做。只需使用 LinearLayout


来源是我,因为我在过去 2 或 3 年一直负责 ListView :) ListView 做了很多额外的工作来优化适配器的使用。试图解决它仍然会导致 ListView 做很多 LinearLayout 不必做的工作。我不会详细介绍,因为这里没有足够的空间来解释 ListView 的实现。这也不会解决 ListView 在触摸事件方面的行为方式。也不能保证如果您的 hack 今天有效,它在未来的版本中仍然有效。使用 LinearLayout 真的不会有太多的工作。
嗯,这是一个合理的回应。如果我可以分享一些反馈,我将拥有更重要的 iPhone 体验,而且我可以告诉你,Apple 的文档在性能特征和用例(或反模式)方面写得更好。总体而言,Android 文档更加分散且不那么集中。我知道这背后有一些原因,但这是一个漫长的讨论,所以如果你觉得有必要聊聊,请告诉我。
您可以轻松编写从适配器创建 LinearLayout 的静态方法。
这不是How can I put a ListView into a ScrollView without it collapsing?的答案...您可以告诉您不应该这样做,但也可以回答如何做,这是一个很好的答案。您知道 ListView 除了滚动之外还有其他一些很酷的功能,这些功能是 LinearLayout 所没有的。
如果您有一个包含 ListView + 其他视图的区域,并且您希望所有内容都作为一个滚动,该怎么办?这似乎是在 ScrollView 中放置 ListView 的合理用例。用 LinearLayout 替换 ListView 不是解决方案,因为这样您就不能使用适配器。
A
Atul Bhardwaj

这肯定会工作............
您只需将布局 XML 文件中的 <ScrollView ></ScrollView> 替换为 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;
    }
}

这是非常好的。它还可以在 ScrollView 中放置 Google Maps Fragment,并且它仍然可以放大/缩小。谢谢。
有没有人注意到这种方法的任何缺点?恐怕它很简单:o
不错的解决方案,应该是公认的答案+1!我将它与下面的答案(来自 djunod)混合在一起,效果很好!
这是唯一让我滚动和点击同时工作的孩子的修复程序。非常感谢
C
Catalina

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


这是最优雅和最恰当的答案恕我直言。有关此方法的更多详细信息,请参阅此答案:stackoverflow.com/a/20475821/1221537
d
djunod

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

这是基于 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();
}

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


当列表项的大小可变时,它不起作用,即一些扩展多行的文本视图。有什么解决办法吗?
在这里查看我的评论 - 它提供了完美的解决方案 - stackoverflow.com/a/23315696/1433187
使用 API 19 为我工作,但不适用于 API 23:这里它在 listView 下方添加了很多空白。
J
Jason Y

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

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

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

它正确测量所有内容(填充,分隔符)它在测量过程中测量 ListView 由于#2,它可以正确处理宽度或项目数量的变化,而无需任何额外的代码

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

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

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


也适用于列表视图中的可变高度项目!
@Jason Y 这是什么,Integer.MAX_VALUE >> 4 是什么意思?什么是4??
@Jason Y 对我来说,它只有在我将 Integer.MAX_VALUE >> 2 更改为 Integer.MAX_VALUE >> 21 后才起作用。为什么会这样?它也适用于 ScrollView 而不是 NestedScrollView。
T
TalkLittle

它有一个内置设置。在滚动视图上:

android:fillViewport="true"

在 Java 中,

mScrollView.setFillViewport(true);

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


正如他演示的那样,这适用于 LinearLayout。它不适用于 ListView。根据该帖子的日期,我想他写它是为了提供他的替代方案的示例,但这并不能回答问题。
这是快速解决方案
D
Dedaniya HirenKumar

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

在您的布局资源文件中

<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>

在 Java 文件中

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


A
Ashish Saini

我们不能同时使用两个滚动。我们将得到 ListView 的总长度并用总高度扩展 listview。然后我们可以直接在 ScrollView 中添加 ListView 或使用 LinearLayout 因为 ScrollView 直接有一个孩子。在您的代码中复制 setListViewHeightBasedOnChildren(lv) 方法并展开列表视图,然后您可以在滚动视图中使用列表视图。 \布局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>

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

好修复!它根据需要工作。谢谢。在我的情况下,我在列表之前有一大堆视图,完美的解决方案是在视图上只有一个垂直滚动。
J
JackA

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

从 Lollipop 开始,您可以使用

yourtListView.setNestedScrollingEnabled(true);

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


这对我在 AppCompat 活动中的 DialogFragment 中的 API 22 列表视图没有任何影响。他们仍然崩溃。 @Phil Ryan 的 answer 进行了一些修改。
C
Cheryl Simon

您不应该将 ListView 放在 ScrollView 中,因为 ListView 已经是 ScrollView。这就像将 ScrollView 放在 ScrollView 中一样。

你想达到什么目的?


我正在尝试在 ScrollView 中实现 ListView。我不希望我的 ListView 可滚动,但没有简单的属性可以设置 myListView.scrollEnabled = false;
正如 Romain Guy 所说,这在很大程度上不是 ListView 的目的。 ListView 是一种针对显示一长串项目进行了优化的视图,具有许多复杂的逻辑来延迟加载视图、重用视图等。如果您告诉 ListView 不要滚动,这些都没有意义。如果您只想要垂直列中的一堆项目,请使用 LinearLayout。
问题是 ListView 提供了许多解决方案,包括您提到的那个。适配器简化的大部分功能是关于数据管理和控制,而不是 UI 优化。 IMO 应该有一个简单的 ListView,和一个更复杂的列表项重用和东西。
绝对同意。请注意,将现有适配器与 LinearLayout 一起使用很容易,但我想要 ListView 提供的所有好东西,比如分隔线,而且我不想手动实现。
@ArtemRussakovskii 您能否解释一下用现有适配器将 ListView 替换为 LinearLayout 的简单方法?
A
Abandoned Cart

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

在哪里调用这个方法?
创建列表视图并设置适配器之后。
这在长列表中变得太慢了。
@cakan 它也是 2 年前编写的,作为一种解决方法,允许一起使用某些并不真正意味着组合的 UI 组件。您在现代 Android 中添加了不必要的开销,因此可以预期列表变大时会变慢。
P
Phil Ryan

我将@DougW 的 Utility 转换为 C#(在 Xamarin 中使用)。以下内容适用于列表中的固定高度项目,并且大部分都很好,或者至少是一个好的开始,如果只有一些项目比标准项目大一点。

// You will need to put this Utility class into a code file including various
// libraries, I found that I needed at least System, Linq, Android.Views and 
// Android.Widget.
using System;
using System.Linq;
using Android.Views;
using Android.Widget;

namespace UtilityNamespace  // whatever you like, obviously!
{
    public class Utility
    {
        public static void setListViewHeightBasedOnChildren (ListView listView)
        {
            if (listView.Adapter == null) {
                // pre-condition
                return;
            }

            int totalHeight = listView.PaddingTop + listView.PaddingBottom;
            for (int i = 0; i < listView.Count; i++) {
                View listItem = listView.Adapter.GetView (i, null, listView);
                if (listItem.GetType () == typeof(ViewGroup)) {
                    listItem.LayoutParameters = new LinearLayout.LayoutParams (ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
                }
                listItem.Measure (0, 0);
                totalHeight += listItem.MeasuredHeight;
            }

            listView.LayoutParameters.Height = totalHeight + (listView.DividerHeight * (listView.Count - 1));
        }
    }
}

感谢@DougW,当我不得不使用 OtherPeople'sCode 时,这让我摆脱了困境。 :-)


一个问题:你什么时候调用了 setListViewHeightBasedOnChildren() 方法?因为它并不总是对我有用。
@AlexandreD 创建项目列表后,您调用 Utility.setListViewHeightBasedOnChildren(yourListView) 。即确保您首先创建了您的列表!我发现我正在制作一个列表,我什至在 Console.WriteLine 中显示我的项目......但不知何故,这些项目在错误的范围内。一旦我弄清楚了这一点,并确保我的列表有合适的范围,这就像一个魅力。
事实是我的列表是在我的 ViewModel 中创建的。在调用 Utility.setListViewHeightBasedOnChildren(yourListView) 之前,如何等待在视图中创建列表?
S
Shashank Kapsime

之前是不可能的。但是随着新的 Appcompat 库和 Design 库的发布,这可以实现。

您只需使用 NestedScrollView https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

我不知道它是否适用于 Listview,但适用于 RecyclerView。

代码片段:

<android.support.v4.widget.NestedScrollView 
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</android.support.v4.widget.NestedScrollView>

酷,我还没有尝试过,所以我无法确认内部 ListView 不会崩溃,但看起来这可能是意图。很遗憾没有在责备日志中看到罗曼盖伊;)
这适用于 recyclerView 但不适用于 listView。谢谢
P
PSchuette

嘿,我有一个类似的问题。我想显示一个不滚动的列表视图,我发现操作参数有效但效率低下,并且在不同设备上的行为会有所不同..因此,这是我的日程安排代码的一部分,它实际上非常有效地执行此操作.

db = new dbhelper(this);

 cursor = db.dbCursor();
int count = cursor.getCount();
if (count > 0)
{    
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.layoutId);
startManagingCursor(YOUR_CURSOR);

YOUR_ADAPTER(**or SimpleCursorAdapter **) adapter = new YOUR_ADAPTER(this,
    R.layout.itemLayout, cursor, arrayOrWhatever, R.id.textViewId,
    this.getApplication());

int i;
for (i = 0; i < count; i++){
  View listItem = adapter.getView(i,null,null);
  linearLayout.addView(listItem);
   }
}

注意:如果您使用它,notifyDataSetChanged(); 将无法按预期工作,因为不会重绘视图。如果您需要解决方法,请执行此操作

adapter.registerDataSetObserver(new DataSetObserver() {

            @Override
            public void onChanged() {
                super.onChanged();
                removeAndRedrawViews();

            }

        });

A
Ali

在 ScrollView 中使用 ListView 时有两个问题。

1- ListView 必须完全展开到其子级高度。这个 ListView 解决了这个问题:

public class ListViewExpanded extends ListView
{
    public ListViewExpanded(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        setDividerHeight(0);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST));
    }
}

分隔线高度必须为 0,改为在行中使用填充。

2- ListView 消耗触摸事件,因此 ScrollView 不能像往常一样滚动。这个 ScrollView 解决了这个问题:

public class ScrollViewInterceptor extends ScrollView
{
    float startY;

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e)
    {
        onTouchEvent(e);
        if (e.getAction() == MotionEvent.ACTION_DOWN) startY = e.getY();
        return (e.getAction() == MotionEvent.ACTION_MOVE) && (Math.abs(startY - e.getY()) > 50);
    }
}

这是我发现的最好的方法!


b
brokedid

我使用的一个解决方案是,将 ScrollView 的所有内容(应该在 listView 的上方和下方)添加为 ListView 中的 headerView 和 footerView。

所以它的工作原理是,convertview 也应该是这样。


C
Community

感谢Vinay's code,这是我的代码,用于当您在滚动视图中没有列表视图但您需要类似的东西时

LayoutInflater li = LayoutInflater.from(this);

                RelativeLayout parent = (RelativeLayout) this.findViewById(R.id.relativeLayoutCliente);

                int recent = 0;

                for(Contatto contatto : contatti)
                {
                    View inflated_layout = li.inflate(R.layout.header_listview_contatti, layout, false);


                    inflated_layout.setId(contatto.getId());
                    ((TextView)inflated_layout.findViewById(R.id.textViewDescrizione)).setText(contatto.getDescrizione());
                    ((TextView)inflated_layout.findViewById(R.id.textViewIndirizzo)).setText(contatto.getIndirizzo());
                    ((TextView)inflated_layout.findViewById(R.id.textViewTelefono)).setText(contatto.getTelefono());
                    ((TextView)inflated_layout.findViewById(R.id.textViewMobile)).setText(contatto.getMobile());
                    ((TextView)inflated_layout.findViewById(R.id.textViewFax)).setText(contatto.getFax());
                    ((TextView)inflated_layout.findViewById(R.id.textViewEmail)).setText(contatto.getEmail());



                    RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);

                    if (recent == 0)
                    {
                        relativeParams.addRule(RelativeLayout.BELOW, R.id.headerListViewContatti);
                    }
                    else
                    {
                        relativeParams.addRule(RelativeLayout.BELOW, recent);
                    }
                    recent = inflated_layout.getId();

                    inflated_layout.setLayoutParams(relativeParams);
                    //inflated_layout.setLayoutParams( new RelativeLayout.LayoutParams(source));

                    parent.addView(inflated_layout);
                }

relativeLayout 保持在 ScrollView 内,因此它全部变为可滚动的 :)


C
Community

这是对 @djunodanswer 的小修改,我需要让它完美运行:

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

:嗨,您的答案大部分时间都有效,但如果列表视图具有标题视图和脚视图,则它不起作用。你能检查一下吗?
当列表项的大小可变时,我需要一个解决方案,即一些扩展多行的文本视图。有什么建议吗?
C
Community

试试这个,这对我有用,我忘了我在哪里找到它,在堆栈溢出的某个地方,我不是在这里解释它为什么不起作用,但这是答案:)。

    final ListView AturIsiPulsaDataIsiPulsa = (ListView) findViewById(R.id.listAturIsiPulsaDataIsiPulsa);
    AturIsiPulsaDataIsiPulsa.setOnTouchListener(new ListView.OnTouchListener() 
    {
        @Override
        public boolean onTouch(View v, MotionEvent event) 
        {
            int action = event.getAction();
            switch (action) 
            {
                case MotionEvent.ACTION_DOWN:
                // Disallow ScrollView to intercept touch events.
                v.getParent().requestDisallowInterceptTouchEvent(true);
                break;

                case MotionEvent.ACTION_UP:
                // Allow ScrollView to intercept touch events.
                v.getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }

            // Handle ListView touch events.
            v.onTouchEvent(event);
            return true;
        }
    });
    AturIsiPulsaDataIsiPulsa.setClickable(true);
    AturIsiPulsaDataIsiPulsa.setAdapter(AturIsiPulsaDataIsiPulsaAdapter);

编辑!,我终于找到了我得到代码的地方。这里 ! :ListView inside ScrollView is not scrolling on Android


这涵盖了如何防止丢失滚动视图交互,但问题是关于保持列表视图高度。
A
Alécio Carvalho

虽然建议的 setListViewHeightBasedOnChildren() 方法在大多数情况下都有效,但在某些情况下,特别是对于很多项目,我注意到最后一个元素没有显示。所以我决定模仿 ListView 行为的一个简单版本,以便重用任何 Adapter 代码,这里是 ListView 替代方案:

import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ListAdapter;

public class StretchedListView extends LinearLayout {

private final DataSetObserver dataSetObserver;
private ListAdapter adapter;
private OnItemClickListener onItemClickListener;

public StretchedListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setOrientation(LinearLayout.VERTICAL);
    this.dataSetObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            syncDataFromAdapter();
            super.onChanged();
        }

        @Override
        public void onInvalidated() {
            syncDataFromAdapter();
            super.onInvalidated();
        }
    };
}

public void setAdapter(ListAdapter adapter) {
    ensureDataSetObserverIsUnregistered();

    this.adapter = adapter;
    if (this.adapter != null) {
        this.adapter.registerDataSetObserver(dataSetObserver);
    }
    syncDataFromAdapter();
}

protected void ensureDataSetObserverIsUnregistered() {
    if (this.adapter != null) {
        this.adapter.unregisterDataSetObserver(dataSetObserver);
    }
}

public Object getItemAtPosition(int position) {
    return adapter != null ? adapter.getItem(position) : null;
}

public void setSelection(int i) {
    getChildAt(i).setSelected(true);
}

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
    this.onItemClickListener = onItemClickListener;
}

public ListAdapter getAdapter() {
    return adapter;
}

public int getCount() {
    return adapter != null ? adapter.getCount() : 0;
}

private void syncDataFromAdapter() {
    removeAllViews();
    if (adapter != null) {
        int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            View view = adapter.getView(i, null, this);
            boolean enabled = adapter.isEnabled(i);
            if (enabled) {
                final int position = i;
                final long id = adapter.getItemId(position);
                view.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        if (onItemClickListener != null) {
                            onItemClickListener.onItemClick(null, v, position, id);
                        }
                    }
                });
            }
            addView(view);

        }
    }
}
}

T
TacoEater

这些答案都是错误的!!!如果您尝试将列表视图放在滚动视图中,您应该重新考虑您的设计。您正在尝试将 ScrollView 放入 ScrollView。干扰列表会损害列表性能。它被Android设计成这样。

如果您真的希望列表与其他元素在同一个滚动中,您所要做的就是使用适配器中的简单 switch 语句将其他项目添加到列表顶部:

class MyAdapter extends ArrayAdapter{

    public MyAdapter(Context context, int resource, List objects) {
        super(context, resource, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
         ViewItem viewType = getItem(position);

        switch(viewType.type){
            case TEXTVIEW:
                convertView = layouteInflater.inflate(R.layout.textView1, parent, false);

                break;
            case LISTITEM:
                convertView = layouteInflater.inflate(R.layout.listItem, parent, false);

                break;            }


        return convertView;
    }


}

列表适配器可以处理所有事情,因为它只呈现可见的内容。


m
majinnaibu

如果 LinearLayout 有一个 setAdapter 方法,这整个问题就会消失,因为当你告诉别人使用它时,替代方案将是微不足道的。

如果您真的想要在另一个滚动视图中滚动 ListView,这将无济于事,否则这至少会给您一个想法。

您需要创建一个自定义适配器来组合您想要滚动的所有内容并将 ListView 的适配器设置为此。

我没有方便的示例代码,但如果你想要类似的东西。

<ListView/>

(other content)

<ListView/>

然后,您需要创建一个表示所有这些内容的适配器。 ListView/Adapters 也足够聪明,可以处理不同的类型,但您需要自己编写适配器。

android UI API 并没有其他平台那么成熟,所以它没有其他平台那么好。此外,在 android 上做某事时,您需要处于 android (unix) 思维模式,在这种思维模式下,您希望做任何事情,您可能必须组装较小部分的功能并编写一堆自己的代码来实现它工作。


D
Durgadass S

当我们将 ListView 放在 ScrollView 中时,会出现两个问题。一个是 ScrollView 在 UNSPECIFIED 模式下测量其子项,因此 ListView 设置自己的高度以仅容纳一个项目(我不知道为什么),另一个是 ScrollView 拦截触摸事件,因此 ListView 不滚动.

但是我们可以ListView 放在 ScrollView 中,并提供一些解决方法。 This post,我解释了解决方法。通过这种解决方法,我们还可以保留 ListView 的回收功能。


S
Saksham

与其将 listview 放在 Scrollview 中,不如将 listview 和 Scrollview 的打开之间的其余内容作为单独的视图,并将该视图设置为 listview 的标题。所以你最终将只负责 Scroll 的列表视图。


G
Garg's

This 库是解决问题的最简单、最快捷的方法。


Z
Zohab Ali

切勿在 scrollview 中使用 listview。相反,您应该使用 NestedScrollView 作为父级并在其中使用 RecyclerView .... 因为它处理了很多滚动问题


m
myforums

这是我计算列表视图总高度的代码版本。这个对我有用:

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

    int totalHeight = 0;
    int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(BCTDApp.getDisplaySize().width, View.MeasureSpec.AT_MOST);
    int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        if (listItem instanceof ViewGroup) listItem.setLayoutParams(lp);
        listItem.measure(widthMeasureSpec, heightMeasureSpec);
        totalHeight += listItem.getMeasuredHeight();
    }

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