[android] How to use RecyclerView inside NestedScrollView?



Answers

UPDATE

Since Android Support Library 23.2.0 there were added method setAutoMeasureEnabled(true) for LayoutManagers. It makes RecyclerView to wrap it's content and works like a charm.
http://android-developers.blogspot.ru/2016/02/android-support-library-232.html

So just add something like this:

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


Old answer (not recommended)

You can use RecyclerView inside NestedScrollView. First of all you should implement your own custom LinearLayoutManager, it makes your RecyclerView to wrap its content. For example:

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

After that use this LayoutManager for your RecyclerView

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

But you also should call those two methods:

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

Here setNestedScrollingEnabled(false) disable scrolling for RecyclerView, so it doesn't intercept scrolling event from NestedScrollView. And setHasFixedSize(false) determine that changes in adapter content can change the size of the RecyclerView

Important note: This solution is little buggy in some cases and has problems with perfomance, so if you have a lot of items in your RecyclerView I'd recommend to use custom LinearLayout-based implementation of list view, create analogue of Adapter for it and make it behave like ListView or RecyclerView

Question

How to use RecyclerView inside NestedScrollView? RecyclerView content is not visible after setting adapter.

UPDATE layout code updated.

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



You can use android:fillViewport="true" to make NestedScrollView measure the RecyclerView. The RecyclerView will fill the remaining height. so if you want to scroll the NestScrollView, you can set the RecyclerView's minHeight.




Here is the code that I'm using to avoid scrolling issues:

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



This is what working for me

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



There is a simple and testing code u may check

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



I have Viewpager and RecyclerView inside the NestedScrollView. After adding below lines

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

I solved slow scroll and scroll lag issue.




You can't use a recycler view within a nested scroll view. It's not intended to contain further scrollable views but it's because it's a child of a scrolling layout itself that you need the nested scrolling view. I had the same issue but in the end I moved my textview to be a headerview within the recyclerview, made the recyclerview a direct child of the coordinator layout and deleted the nested scroll view. Then all my problems were gone.




Links