关于android:如何在NestedScrollView中使用RecyclerView?

How to use RecyclerView inside NestedScrollView?

如何在NestedScrollView中使用RecyclerView
设置适配器后,RecyclerView内容不可见。

UPDATE布局代码已更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<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>


将您的recyclerView替换为

1
2
3
4
5
<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" />

这里,

1
app:layout_behavior="@string/appbar_scrolling_view_behavior"

将管理其余的事情。

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


更新1

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

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

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

更新2

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

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

旧答案(不推荐)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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

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

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

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

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

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


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 use viewType and add all other views that need to scroll to RecyclerView rather than using RecyclerView in Scrollview`。性能影响将很高。

简单起见,"它在所有子视图中都充当LinearLayout"


您可以使用android:fillViewport="true"使NestedScrollView测量RecyclerViewRecyclerView将填充剩余的高度。因此,如果要滚动NestScrollView,可以设置RecyclerViewminHeight


setAdapter本身起作用之前,只需添加recyclerView.setNestedScrollingEnabled(false);即可。我没有在任何地方添加app:layout_behavior="@string/appbar_scrolling_view_behavior",也没有设置任何自定义布局管理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<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>


这对我有用

1
2
3
4
5
6
7
8
9
10
11
12
<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>


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

1
2
3
4
5
6
7
8
9
<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>

对于androidx,它称为androidx.core.widget.NestedScrollView-并在启用属性isScrollContainermeasureAllChildren的情况下像黄油一样滚动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 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>

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

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


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

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

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

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

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

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

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

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


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

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

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


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

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

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

并在您的Java文件中:

1
2
3
4
5
6
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);可以解决问题。

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


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


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


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

1
mRecyclerView.setNestedScrollingEnabled(false);

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

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

如果您在NestedScrollView中使用RecyclerView ScrollListener,则同时使用addOnScrollListener侦听器将无法正常工作。

使用此代码。

1
2
3
4
5
6
7
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
              ......
        }
    });

此代码在NestedScrollView内的RecyclerView ScrollListener正常工作。

谢谢


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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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"/>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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>

就我而言,NestedScrollview的子级是ConstraintLayout。 它没有按预期工作,我将其替换为LinearLayout。 也许对某人有帮助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<androidx.core.widget.NestedScrollView
  android:id="@+id/nestedScrollView"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:descendantFocusability="blocksDescendants">

    <androidx.recyclerview.widget.RecyclerView
      android:id="@+id/recyclerView"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:nestedScrollingEnabled="false"
      app:layout_constraintBottom_toTopOf="@+id/divider"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/tinTitle"/>

  </LinearLayout>
</androidx.core.widget.NestedScrollView>

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

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

基本上,您可以在任何包中找到NestedRecyclerView,在项目中输入utils,然后像下面那样创建您的recyclerview

1
2
3
4
 <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


Solution for not to load all recyclew view child at once inside nested
scroll view.

Benfits:

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

    in your xml instead of using match_parent or wrap_content for
    recycleview use fix height.


    带NestedScrollView的RecyclerView

    1
    2
    3
    4
    5
        <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" />