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

更新布局代码。

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

当前回答

如果你想在NestedScrollView中使用RecyclerView,这是一个简单的技巧,只需设置:

RecyclerView

recyclerView.setHasFixedSize (false) (java / kt) android: nestedScrollingEnabled = " false " android: layout_height = " wrap_content " android: overScrollMode =“从不”

嵌套滚动视图

android:fillViewport=“true”

这是我的工作,你可以使用许多RecyclerView在NestedScrollView与此。

其他回答

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

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

基本上你在任何包中都有NestedRecyclerView比如在项目中的utils,然后创建你的recyclerview

 <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

更新1

自Android支持库23.2.0以来,为布局管理器添加了setAutoMeasureEnabled(true)方法。它使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

因为27.1.0的setAutoMeasureEnabled已弃用,所以你应该提供LayoutManager的自定义实现,覆盖方法isAutoMeasureEnabled()

但是经过多次使用RecyclerView后,我强烈建议不要在包装模式下使用它,因为这不是它的目的。尝试使用普通的单个RecyclerView与几个项目类型重构整个布局。或者使用线性布局的方法,我在下面描述作为最后的手段

旧答案(不推荐)

你可以在NestedScrollView中使用RecyclerView。 首先,你应该实现你自己的自定义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的大小

重要提示:这个解决方案是小bug在某些情况下,有问题与性能,所以如果你有很多项目在你的RecyclerView,我建议使用自定义线性布局的实现列表视图,为它创建类似的适配器,使其行为像ListView或RecyclerView

至少早在Material Components 1.3.0-alpha03中,RecyclerView是否嵌套并不重要(在ScrollView或NestedScrollView之外的东西中)。只要把app:layout_behavior="@string/appbar_scrolling_view_behavior"放在它的顶层父元素上,它是CoordinatorLayout中的AppBarLayout的兄弟元素。

当我在Jetpack导航中使用单个活动架构时,这已经为我工作了,其中所有片段都共享来自活动布局的相同AppBar。我将FragmentContainer作为CoordinatorLayout的直接子,它也包含AppBarLayout,如下所示。各个片段中的RecyclerViews正常滚动,AppBar折叠起来并按预期重新出现。

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/coordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            app:defaultNavHost="true"
            app:navGraph="@navigation/mobile_navigation"/>

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appbar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:liftOnScroll="true">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="?attr/actionBarSize"
                android:theme="?attr/actionBarTheme"
                app:layout_scrollFlags="scroll|enterAlways|snap" />

        </com.google.android.material.appbar.AppBarLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

liftOnScroll(用于应用程序栏看起来像他们有零海拔时,在页面的顶部)工作,如果每个片段传递其RecyclerView的ID到AppBarLayout。liftOnScrollTargetViewId in Fragment.onResume。如果Fragment没有滚动,则传递0。

这就是对我有效的方法

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

保持recyclerview的回收特性并避免recyclerview加载所有数据的一个解决方案是在recyclerview本身设置一个固定的高度。通过这样做,recyclerview只能加载它的高度所能显示给用户的东西,这样当你滚动到底部/顶部时,循环它的元素。