Horizontal RecyclerView inside a Vertical RecyclerView

This is a simple tutorial to develop a scrolling horizontal RecyclerView inside a vertical RecyclerView in Android.

First, we will create the main layout that shows the vertical RecyclerView:

courses_fragment.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:paddingLeft="15dp"
    android:paddingRight="15dp">

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id=“@+id/vertical_courses_list"
        android:visibility="gone"/>


</RelativeLayout>

It’s a simple relative layout with a RecyclerView in it.

Now let’s create the layout for each item of the vertical RecyclerView:

courses_item.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="15dp"
    android:layout_marginBottom="15dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true">

        <TextView
            android:id="@+id/course_item_name_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/RegularTextStyle"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/horizontal_list"
            android:layout_width="match_parent"
            android:layout_marginTop="5dp"
            android:layout_height="30dp"
            android:visibility="visible"/>

    </LinearLayout>
    <ImageView
    		android:layout_width="wrap_content"
    		android:layout_height="wrap_content"
    		android:layout_alignParentRight="true"
    		android:layout_centerVertical="true"
    		android:src="@drawable/ic_play_circle_outline_white_24dp"
    		android:tint="@color/colorAccent"/>


</RelativeLayout>

We’ve made a Relative layout with a TextView that displays a Name, an Arrow to the right, and an inside RecyclerView below the Name.

Note: it is important that the inner RecyclerView has layout_height assigned, it won’t work with match_parent or wrap_content.

The same way we created a layout for each item of the main RecyclerView, we will have to create a new layout for each item of the inner horizontal RecyclerView: 

courses_horizontal_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginRight="3dp">

    <TextView
        android:id="@+id/horizontal_item_text"
        android:textColor="@color/white"
        android:layout_width="match_parent"
        android:padding="5dp"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"/>

</LinearLayout>

Good! We are done with xml files. Now we have to create the adapters for each RecyclerView. Let’s start with inner Horizontal RecyclerView adapter:

HorizontalRVAdapter

public class HorizontalRVAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<String> mDataList;
    private int mRowIndex = -1;

    public HorizontalRVAdapter() {
    }

    public void setData(List<String> data) {
        if (mDataList != data) {
            mDataList = data;
            notifyDataSetChanged();
        }
    }

    public void setRowIndex(int index) {
        mRowIndex = index;
    }

    private class ItemViewHolder extends RecyclerView.ViewHolder {

        private TextView text;

        public ItemViewHolder(View itemView) {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.horizontal_item_text);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        View itemView = LayoutInflater.from(context).inflate(R.layout.courses_horizontal_item, parent, false);
        ItemViewHolder holder = new ItemViewHolder(itemView);
        return holder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder rawHolder, int position) {
        ItemViewHolder holder = (ItemViewHolder) rawHolder;
        holder.text.setText(mDataList.get(position));
        holder.itemView.setTag(position);
    }

    @Override
    public int getItemCount() {
        return mDataList.size();
    }

}

Here we inflate the layout for each item of the horizontal RecyclerView and set data to each view. In this case we only have a TextView which is filled with a String from List<String>, but views and lists can be customized and be more complex according to each need.

Now let’s wrap it all together in the main RecyclerView adapter:

CourseRVAdapter

public class CourseRVAdapter extends RecyclerView.Adapter<CourseRVAdapter.SimpleViewHolder> {

    private final Context mContext;
    private static List<Nugget> mData;
    private static RecyclerView horizontalList;

    public static class SimpleViewHolder extends RecyclerView.ViewHolder {
        public final TextView title;
        private HorizontalRVAdapter horizontalAdapter;

        public SimpleViewHolder(View view) {
            super(view);
            Context context = itemView.getContext();
            title = (TextView) view.findViewById(R.id.course_item_name_tv);
            horizontalList = (RecyclerView) itemView.findViewById(R.id.horizontal_list);
            horizontalList.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
            horizontalAdapter = new HorizontalRVAdapter();
            horizontalList.setAdapter(horizontalAdapter);
        }
    }

    public CourseRVAdapter(Context context, List<Nugget> data) {
        mContext = context;
        if (data != null)
            mData = new ArrayList<>(data);
        else mData = new ArrayList<>();
    }

    public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(mContext).inflate(R.layout.courses_item, parent, false);
        return new SimpleViewHolder(view);
    }

    @Override
    public void onBindViewHolder(SimpleViewHolder holder, final int position) {
        holder.title.setText(mData.get(position).getTitle());
        holder.horizontalAdapter.setData(mData.get(position).getTags()); // List of Strings
        holder.horizontalAdapter.setRowIndex(position);
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }
    
}

And finally, we set the main RecyclerView adapter in our Activity/Fragment:

private CourseRVAdapter adapter;

… 

// Setting RecyclerView
coursesRecyclerView.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
coursesRecyclerView.setLayoutManager(llm);
// nuggetsList is an ArrayList of Custom Objects, in this case  Nugget.class
adapter = new CourseRVAdapter(getActivity(), nuggetsList);
coursesRecyclerView.setAdapter(adapter);

15 thoughts on “Horizontal RecyclerView inside a Vertical RecyclerView

  1. Ben

    Thank you so much for your example. It helps a lot. I am using it to show the details of a long list of weather stations. Unfortunately after some scrolling the list starts to show wrong entries. I was debugging my data until it arrives in my inner Recyclerview and found no errors. My guess is that a recycler view within a recycler view somehow leaks data. Have you had such problems and found a way to deal with it?

    Ben

    Like

  2. Neeraj

    Hi, Thanks for the great tutorial.
    There is a bug in when we scroll down and see some horizontal item’s are already scrolled with some random position ? What is use of mRowIndex??

    Like

  3. Hi, hopefully you still read old blog post comments. Firstly, this is article was a tremendous help, I had tried and failed to used the sectioned recyclerview for my tv programme guide app but this works and it’s far simpler to implement.

    Q. How can I scroll the all the recyclerviews horizontally as one rather than individually.

    Thanks!

    Like

Leave a comment