如何在NestedScrollView中使用RecyclerView?


210

RecyclerView里面怎么用NestedScrollViewRecyclerView设置适配器后,内容不可见。

UPDATE布局代码已更新。

<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/keyline_1">

    </RelativeLayout>

    <View
        android:id="@+id/separator"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#e5e5e5" />

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

</LinearLayout>

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


@Mecid没有理由在ScrollView(或NestedScrollView)中使用RecyclerView。
加布里埃尔·马里奥蒂

2
@Mecid,切勿将可滚动视图放在另一个可滚动视图中。这是Android中的一般规则。
加布里埃尔·马里奥蒂

6
@GabrieleMariotti我知道这个规则,但是NestedScrollView开发出来可以解决这个问题。
Mecid

1
这是不正确的。NestedScrollView和ScrollView一样,但是它既支持嵌套滚动父级又充当子级。在您的情况下,您必须定义自己的滚动行为。
加布里埃尔·马里奥蒂

Answers:


216

将您的recyclerView替换为

<android.support.v7.widget.RecyclerView
    android:id="@+id/conversation"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

这里,

app:layout_behavior="@string/appbar_scrolling_view_behavior"

将管理其余的事情。

还有一件事,无需将您的recyclerView放在NestedScrollView中


33
我不明白为什么这个解决方案得不到最多的支持。不能用来实现,NestedScrollView而只能用来实现app:layout_behavior="@string/appbar_scrolling_view_behavior"。这是关键!
哈塔2015年

3
@RahulUpadhyay这对我有用,但是我认为您的最后一行是错误的,如果它在NestedScrollView内,则根本不会显示RecyclerView。但是,您是超级巨星:D
Leon Leon

3
有用!只需更新recyclerview库即可编译'com.android.support:recyclerview-v7:+'–
Fabio Guerra

28
该解决方案的目的是与使用CoordinatorLayoutAppBarLayout和设置适当layout_scrollFlags的子视图(一个或多个)应与该滚动RecyclerView。不知道为什么从答案中忽略了这一点。否则,这是一个很好且简单的解决方案。
Sanvywell '16

9
我认为了解RecyclerView的回收在这里不起作用非常重要。结果,如果您的项目列表很长,它将以明显的UI冻结出现。
加凯特

121

更新1

自Android支持库23.2.0起,添加了setAutoMeasureEnabled(true)用于LayoutManagers的方法。它使RecyclerView可以包装其内容,并且像一个饰物一样工作。
http://android-developers.blogspot.ru/2016/02/android-support-library-232.html

因此,只需添加如下内容:

    LayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setAutoMeasureEnabled(true);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setNestedScrollingEnabled(false);


更新2

由于setAutoMeasureEnabled不建议使用27.1.0 ,因此您应该使用覆盖的方法提供LayoutManager的自定义实现isAutoMeasureEnabled()

但是在很多情况下使用RecyclerView之后,我强烈建议不要在包装模式下使用它,因为这不是它的预期用途。尝试使用具有多个项目类型的普通单个RecyclerView重构整个布局。或使用我在下面描述的最后一种方法使用LinearLayout


旧答案(不推荐)

你可以RecyclerView在里面使用NestedScrollView。首先,您应该实现自己的custom LinearLayoutManager,它使您RecyclerView可以包装其内容。例如:

public class WrappingLinearLayoutManager extends LinearLayoutManager
{

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

    private int[] mMeasuredDimension = new int[2];

    @Override
    public boolean canScrollVertically() {
        return false;
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
            int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);

        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        int width = 0;
        int height = 0;
        for (int i = 0; i < getItemCount(); i++) {
            if (getOrientation() == HORIZONTAL) {
                measureScrapChild(recycler, i,
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        heightSpec,
                        mMeasuredDimension);

                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                measureScrapChild(recycler, i,
                        widthSpec,
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        mMeasuredDimension);

                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }

        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
            int heightSpec, int[] measuredDimension) {

        View view = recycler.getViewForPosition(position);
        if (view.getVisibility() == View.GONE) {
            measuredDimension[0] = 0;
            measuredDimension[1] = 0;
            return;
        }
        // For adding Item Decor Insets to view
        super.measureChildWithMargins(view, 0, 0);
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(
                widthSpec,
                getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view),
                p.width);
        int childHeightSpec = ViewGroup.getChildMeasureSpec(
                heightSpec,
                getPaddingTop() + getPaddingBottom() + getDecoratedTop(view) + getDecoratedBottom(view),
                p.height);
        view.measure(childWidthSpec, childHeightSpec);

        // Get decorated measurements
        measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
        measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
    }
}

之后,将其LayoutManager用于您的RecyclerView

recyclerView.setLayoutManager(new WrappingLinearLayoutManager(getContext()));

但是您还应该调用这两个方法:

recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);

在此setNestedScrollingEnabled(false)禁用的滚动RecyclerView,因此它不会拦截来自的滚动事件NestedScrollView。并setHasFixedSize(false)确定适配器内容的更改可以更改RecyclerView

重要说明:此解决方案在某些情况下几乎没有错误,并且在性能方面存在问题,因此,如果您有很多项目,RecyclerView建议您使用LinearLayout基于自定义的列表视图实现,为其创建适配器的类似物并使其表现得像ListViewRecyclerView


19
警告!如果您的商品中有很多物品,请不要使用此功能RecyclerView
Brais Gabin 2015年

1
@BraisGabin,您是对的:setHasFixedSize(false)会引起RecyclerView的多次刷新,因此,如果其中包含很多项,则它将非常缓慢地工作。在那种情况下,我将LinearLayout与一种自定义“适配器”一起使用,以使其表现得像ListView或RecyclerView
smbd uknow

1
非常感谢!节省了我几个小时。顺便说一句,最后一个项目的装饰不会与此布局管理器一起显示(并且仅与此一起显示),可能会出错吗?
Jjang 2015年

2
从API级别27.1.0 开始
Dika,

2
改写boolean isAutoMeasureEnabled()。返回布局的测量遍是否应使用RecyclerView的AutoMeasure机制,还是应由LayoutManager的onMeasure(Recycler,State,int,int)实现来完成。默认情况下,此方法返回false(它实际上返回传递给不赞成使用的setAutoMeasureEnabled(boolean)的值),如果LayoutManager要由RecyclerView自动测量,则应重写此方法以返回true。如果重写此方法以返回true,则不应重写onMeasure(Recycler,State,int,int)。
Peterstev Uremgba '18

100

1)您需要使用以上的支持库23.2.0(或)

2)和RecyclerView高度将为wrap_content

3) recyclerView.setNestedScrollingEnabled(false)

但是这样做的回收模式无效。(即所有的意见,将立即被加载,因为wrap_content完全需要的高度RecyclerView,因此将吸引所有子Views at once. No view will be recycled). Try not to use this pattern unless it is really required. Try to useviewType and add all other views that need to scroll toRecyclerView rather than usingRecyclerView inScrollview`,对性能的影响将是非常高的。

简单起见,“它就像LinearLayout所有子视图一样起作用”


6
谢谢@Ashok。设置recyclerView.setNestedScrollingEnabled(false)帮助我进行了平滑滚动。
Shubhral

3
这应该是公认的答案!很好地指出没有视图将被回收并且所有视图将被立即绘制。
M'hamed

3
@AshokVarma,我现在正面临同样的问题。但是滚动在recyclerView内运行良好,但是我无法为recyclerview实施无限滚动,因为recyclerview绘制的所有视图会立即出现,因此它会导致继续调用Web服务。所以我该如何处理呢?
Anantha Babu

@AnanthaBabu而不是在scrollview中使用recyclerview。将滚动视图中的所有项目转换为回收者视图中的项目。您可以使用recyclerviews视图类型并绘制不同类型的布局。也有一些很好的库可以处理这些问题(如果您的视图结构非常复杂并且需要可重用的组件,请使用库)。
Ashok Varma

1
@AshokVarma是否有解决回收商行为问题的解决方案?我不希望recyclerview一口气吸引所有孩子。
andro-girl

36

您可以android:fillViewport="true"用来NestedScrollView测量RecyclerView。在RecyclerView将填补剩余的高度。所以如果你要滚动的NestScrollView,你可以设置RecyclerViewminHeight


14
如果您有200个项目的recyclerview,并且执行此操作,则nestedscrollview将展开以填充全部200个项目!
RJFares'Apr 8'17

1
@RJFares从来没有发生过。我尝试一个演示,并使用LayoutInspector检查布局,它运行良好。您可以提供recyclerView的版本吗?
zayn

26

简单地增加recyclerView.setNestedScrollingEnabled(false);setAdapter本身为我工作。我没有添加app:layout_behavior="@string/appbar_scrolling_view_behavior"任何位置,也没有设置任何自定义布局管理器

<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:layout_marginRight="15dp"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/white"
                android:text="Some Text..."
                android:padding="15dp" />

        </LinearLayout>

        <LinearLayout
            android:orientation="vertical"
            android:padding="15dp"
            android:layout_marginTop="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Quick Links"
                android:textColor="@color/black"
                android:textStyle="bold"
                android:textAllCaps="true"
                android:paddingLeft="20dp"
                android:drawableLeft="@drawable/ic_trending_up_black_24dp"
                android:drawablePadding="10dp"
                android:layout_marginBottom="10dp"
                android:textSize="16sp"/>

            <View
                android:layout_width="fill_parent"
                android:layout_height="1dp"
                android:background="#efefef"/>

            <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>

    </LinearLayout>

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

10
但是现在它不再回收视图了吗?
Mark Buikema '17

不,它会回收,不是吗?
Vignesh Sundar

6
不,不是,我尝试了一个非常复杂的布局,其中包含200个项目,nestedscrollview只会扩展到其子项的大小,因此它将扩展,因此不会发生回收。@MarkBuikema您是否设法找到了更好的解决方案?
RJFares'Apr 8'17

20

这对我有用

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.v4.widget.NestedScrollView>

4
您可以删除第二个应用程序:layout_behavior =“ @ string / appbar_scrolling_view_behavior”,因为该行为仅适用于CoordinatorLayout直接子级。
百事可乐

2
此外,重要的是要提到您应该将协调器布局作为根布局。并非总是如此。
加盖

10

您可以检查一个简单的测试代码

<android.support.v4.widget.NestedScrollView
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fillViewport="true"
     app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <android.support.v7.widget.RecyclerView
           android:layout_width="match_parent"
           android:layout_height="match_parent"/>
   </android.support.v4.widget.NestedScrollView>

9

因为androidx它被称为androidx.core.widget.NestedScrollView-并且还滚动了具有属性isScrollContainermeasureAllChildren启用的类似黄油:

<!-- Scrolling Content -->
<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"

    android:isScrollContainer="true"
    android:measureAllChildren="true"

    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fastScrollEnabled="true"
        android:scrollbarStyle="insideInset"
        android:scrollbars="vertical"
        android:splitMotionEvents="false"
        android:verticalScrollbarPosition="right"/>

</androidx.core.widget.NestedScrollView>

7

尝试使用此库-https: //github.com/serso/android-linear-layout-manager

该库的LayoutManager使RecyclerView包装其内容。在这种情况下,RecyclerView将“与内部视图一样大”,因此它将没有滚动条,并且用户将使用NestedScrollView的滚动功能。因此,它不会像“ scrollable inside scrollable”那样模棱两可。


1
这是从这个库中的类LinearLayoutManager的:github.com/serso/android-linear-layout-manager/blob/master/lib/...
忍者编码

6

这是我用来避免滚动问题的代码:

mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.getLayoutManager().setAutoMeasureEnabled(true);
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setHasFixedSize(false);

6

我在NestedScrollView中使用了RecyclerView,它对我有用。我要记住的唯一陷阱是NestedScrollView只接受一个子视图。因此,在我的情况下,我使用了LienearLayout视图组,该视图组容纳了我的RecyclerView以及我需要的许多其他视图。

我将RecyclerView放入NestedScrollView时遇到一个问题。我意识到滚动RecyclerView的内容很费劲。

后来我意识到我的RecyclerView正在接收滚动事件,因此与NestedScrollView的滚动行为发生冲突。

因此,要解决该问题,我必须使用此方法禁用RecyclerView的滚动功能 movieListNewRecyclerView.setNestedScrollingEnabled(false);

您可以在我的Instagram上查看我实际所做的简短视频。这是我的Instagram句柄ofelix03

点击此图片查看我做了什么


5

我在NestedScrollView中有Viewpager和RecyclerView。添加以下行后

recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);

我解决了慢滚动和滚动滞后的问题。


3
不是recyclerView.setHasFixedSize(true);
基诺·克莱顿

3

您不能在嵌套滚动视图中使用回收站视图。它不打算包含其他可滚动视图,而是因为它是滚动布局的子元素,因此您需要嵌套的滚动视图。我遇到了同样的问题,但最后我将textview移到了recyclerview的标题视图中,使recyclerview成为协调器布局的直接子级,并删除了嵌套的滚动视图。然后我所有的问题都消失了。


8
既然RecyclerView工具NestedScrollingChildRecyclerView应该可以通过滚动NestedScrollView
本杰明

我只投票基于纯粹的英语错误而拒绝stackoverflow.com/review/suggested-edits/9907622,但其中可能包含有价值的内容-任何对主题知识了解的人都应该看看并看一下可以清理它。如果那里没有什么有用的,则标记我的此评论以供mods删除。
Mark Amery 2015年

3

如果您使用的是RecyclerView-23.2.1更高版本。以下解决方案将正常工作:

在您的布局中添加RecyclerView,如下所示:

<android.support.v7.widget.RecyclerView
        android:id="@+id/review_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" />

并在您的java文件中:

RecyclerView mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager=new LinearLayoutManager(getContext());
layoutManager.setAutoMeasureEnabled(true);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(new YourListAdapter(getContext()));

layoutManager.setAutoMeasureEnabled(true);将解决问题。

查看此问题此开发者博客以获取更多信息。


使用“ mRecyclerView.setHasFixedSize(true);”时要小心。虽然。只有在运行时不从“回收者”视图中添加或删除项目时,才可以使用它。
加凯特,2016年

2

保留recyclerview的回收功能并避免recyclerview加载所有数据的一种解决方案是在recyclerview本身中设置固定高度。通过这样做,recyclerview仅限于加载,只要它的高度可以显示给用户,就可以在滚动到底部/顶部时回收其元素。


2

有很多好的答案。关键是您必须将设置nestedScrollingEnabledfalse。如上所述,您可以在Java代码中完成此操作:

mRecyclerView.setNestedScrollingEnabled(false);

而且您还有机会在xml代码(android:nestedScrollingEnabled="false")中设置相同的属性:

 <android.support.v7.widget.RecyclerView
 android:id="@+id/recyclerview"
 android:nestedScrollingEnabled="false"
 android:layout_width="match_parent"
 android:layout_height="match_parent" />

2

如果您正在使用RecyclerView ScrollListenerNestedScrollView,如果你同时使用addOnScrollListener监听器不能正常工作。

使用此代码。

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState); 
              ......
        }
    });

此代码工作正常RecyclerView ScrollListenerNestedScrollView

谢谢


1

我必须通过滚动工具栏来实现CoordinatorLayout,这使我整日忙于解决这一问题。我已经通过完全删除NestedScrollView使其工作。所以我只是在根目录使用RelativeLayout。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_nearby"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</RelativeLayout>

1

不要在NestedScrollView中使用recyclerView。可能会导致级联问题!我建议在RecyclerView中使用ItemViewTypes处理多种视图。只需添加一个具有match_parent宽度和高度的RecyclerView。然后在您的recyclerViewAdapter中重写getItemViewType并使用position处理要放大的布局。之后,您可以使用onBindViewHolder方法处理视图持有者。


1
nestedScrollView.setNestedScrollingEnabled(true);

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //...
        }
    });


<androidx.core.widget.NestedScrollView
    android:id="@+id/nested"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    android:layout_below="@id/appBarLayout_orders"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"> 

    <androidx.constraintlayout.widget.ConstraintLayout ...

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

0

我已经使用了这个很棒的扩展(用kotlin编写,但也可以用Java编写)

https://github.com/Widgetlabs/expedition-nestedscrollview

基本上,您可以NestedRecyclerView进入任何包,在项目中输入utils,然后像

 <com.your_package.utils.NestedRecyclerView
      android:id="@+id/rv_test"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

查看Marc Knaup的这篇很棒的文章

https://medium.com/widgetlabs-engineering/scrollable-nestedscrollviews-inside-recyclerview-ca65050d828a


0

不立即在嵌套 滚动视图中加载所有recyclew视图子级的解决方案

Benfits:

  1. 使用嵌套滚动视图时,不能一次加载所有子项。
  2. 您可以使用recycleView.setHasFixedSize(true)以获得更好的性能
  3. 阻止Web服务一次发送所有数据,分页工作正常
  4. 适用于大量数据集。
  5. 适用于低内存设备
  6. 可以继续使用嵌套滚动
  7. 当您的回收视图项目复杂并且需要大量资源时,这将非常有用。

解决:

在您的xml中,而不是将match_parentwrap_content用于recycleview,请使用fix height


-5

带NestedScrollView的RecyclerView

    <android.support.v7.widget.RecyclerView
    android:id="@+id/rv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.