【问题标题】:How can I put a ListView into a ScrollView without it collapsing?如何在不折叠的情况下将 ListView 放入 ScrollView?
【发布时间】:2011-03-30 13:57:27
【问题描述】:

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

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

【问题讨论】:

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

标签: android android-listview android-scrollview


【解决方案1】:

这是我的解决方案。我对 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);
    }
}

【讨论】:

  • 你刚刚重新创建了一个非常昂贵的 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 的 ListViews:int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
  • 不幸的是,当ListView 可以包含可变高度的项目时,这种方法有点缺陷。对getMeasuredHeight() 的调用仅返回目标最小高度,而不是实际高度。我尝试改用getHeight(),但这会返回 0。有人知道如何解决这个问题吗?
【解决方案2】:

使用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 不是解决方案,因为这样您就不能使用适配器。
【解决方案3】:

这肯定行得通............
您只需将布局 XML 文件中的 &lt;ScrollView &gt;&lt;/ScrollView&gt; 替换为 Custom ScrollView,例如 &lt;com.tmd.utils.VerticalScrollview &gt; &lt;/com.tmd.utils.VerticalScrollview &gt;

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)混合在一起,效果很好!
  • 这是唯一让我可以滚动和点击同时工作的孩子的修复方法。非常感谢
【解决方案4】:

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

【讨论】:

【解决方案5】:

很多在 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();
}

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

【讨论】:

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

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 设置为 AT_MOST 并具有非常大的尺寸 (Integer.MAX_VALUE >> 4) 会导致 ListView 测量其所有子级直到给定(非常大)高度并相应地设置其高度。

这比其他解决方案效果更好,原因如下:

  1. 它可以正确测量所有内容(填充、分隔线)
  2. 它在测量过程中测量 ListView
  3. 由于 #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。
【解决方案7】:

它有一个内置设置。在 ScrollView 上:

android:fillViewport="true"

在 Java 中,

mScrollView.setFillViewport(true);

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

【讨论】:

  • 正如他演示的那样,它适用于 LinearLayout。它不适用于 ListView。根据该帖子的日期,我想他写它是为了提供他的替代方案示例,但这并不能回答问题。
  • 这是快速解决方案
【解决方案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();    
    }
}

在您的布局资源文件中

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

【讨论】:

    【解决方案9】:

    我们不能同时使用两个滚动。我们将得到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);
          }
    

    【讨论】:

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

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

    你可以在 Lollipop 上使用

    yourtListView.setNestedScrollingEnabled(true);
    

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

    【讨论】:

    • 这对我在 AppCompat 活动中的 DialogFragment 中的 API 22 列表视图没有任何影响。他们仍然崩溃。 @Phil Ryan 的 answer 进行了一些修改。
    【解决方案11】:

    您不应该将 ListView 放在 ScrollView 中,因为 ListView 已经 ScrollView。所以这就像在 ScrollView 中放置一个 ScrollView。

    你想完成什么?

    【讨论】:

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

    这是 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 中添加了不必要的开销,因此可以预期列表变大时会变慢。
    【解决方案13】:

    我将@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) 之前,如何等待在视图中创建列表?
    【解决方案14】:

    以前是不可能的。但是随着新的 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。谢谢
    【解决方案15】:

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

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

    【讨论】:

      【解决方案16】:

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

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

      【讨论】:

        【解决方案17】:

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

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

        【讨论】:

          【解决方案18】:

          感谢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 中,因此它全部变为可滚动的 :)

          【讨论】:

            【解决方案19】:

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

            【讨论】:

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

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

                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

            【讨论】:

            • 这涵盖了如何防止丢失滚动视图交互,但问题是关于保持列表视图高度。
            【解决方案21】:

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

            【讨论】:

              【解决方案22】:

              所有这些答案都是错误的!!! 如果您尝试将列表视图放在滚动视图中,您应该重新考虑您的设计。您正在尝试将 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;
                  }
              
              
              }
              

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

              【讨论】:

                【解决方案23】:

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

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

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

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

                <ListView/>
                
                (other content)
                
                <ListView/>
                

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

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

                【讨论】:

                  【解决方案24】:

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

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

                  【讨论】:

                    【解决方案25】:

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

                    【讨论】:

                      【解决方案26】:

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

                      【讨论】:

                        【解决方案27】:

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

                        【讨论】:

                          【解决方案28】:

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

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

                          【讨论】:

                            猜你喜欢
                            • 2015-07-08
                            • 1970-01-01
                            • 2016-08-26
                            • 2021-12-25
                            • 1970-01-01
                            • 2015-08-26
                            • 2018-10-01
                            • 2019-05-11
                            • 2015-10-10
                            相关资源
                            最近更新 更多