How to have a ListView/RecyclerView inside a parent RecyclerView?

Solution 1:

I got this issue few days ago and finally solved it. All you have to do is @override the layout manager onMeasure function as below:

CustomLinearLayoutManager

public class CustomLinearLayoutManager extends LinearLayoutManager {

    private static final String TAG = CustomLinearLayoutManager.class.getSimpleName();

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

    public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

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

    @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++) {
            measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);


            if (getOrientation() == HORIZONTAL) {
                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                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) {
        try {
            View view = recycler.getViewForPosition(0);//fix IndexOutOfBoundsException

            if (view != null) {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();

                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight(), p.width);

                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);

                view.measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
                recycler.recycleView(view);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

You can just copy and paste the custom layout manager as above and in set up your recyclerview in the parent adapter like this:

RecyclerView.LayoutManager layoutManager = new CustomLinearLayoutManager(mContext);
holder.childRecyclerView.setLayoutManager(layoutManager);

Remember : Don't use same layoutManager as parent adapter, or error will occur.

Solution 2:

By Android Support Library 23.2 of a support library version 23.2.0. So all WRAP_CONTENT should work correctly.

Please update version of a library in gradle file.

compile 'com.android.support:recyclerview-v7:23.2.0'

This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.

you’ll be required to call setAutoMeasureEnabled(true)

Below is the sample code

RecyclerView.LayoutManager layout = new LinearLayoutManager(context); 
layout.setAutoMeasureEnabled(true);

As setAutoMeasureEnabled is deprecated alternate solution This method was deprecated in API level 27.1.0. Implementors of LayoutManager should define whether or not it uses AutoMeasure by overriding isAutoMeasureEnabled().

Solution 3:

If I understand, you want a RecyclerView with RecyclerView rows. In this case I recommend you to use an expandable RecyclerView, using this lib. Just follow the expandable example within the link.

There are many more cool features in the same lib, like Drag and Drop, Swipeable rows... Watch this less than a minute example video.

You just have to add the lib to the dependencies in your gradle.build file, like following:

dependencies {
    compile 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.7.4'
}

To be able to import the lib in your java files.