How to implement multiple recyclerviews in one layout?

Tom11 picture Tom11 · Jun 5, 2018 · Viewed 7.9k times · Source

I want to create a pane in which there are two RecyclerViews (let's say 'MyItems', 'AllItems'). I have created vertical LinearLayout, in which there are TextView as title and RecyclerView. Something like this:

enter image description here

 <LinearLayout ... >

    <TextView
        android:text="My Items"
        ... />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_items"
        ... />

    <TextView
        android:text="All Items"
        ... />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/all_items"
        ... />

</LinearLayout>

However with this approach, only recyclerViews are scrollable independently, but I need the whole layout to be scrollable only (so firstly it scrolls through first section, then second). I tried to wrap it up in ScrollView and NestedScrollView, but the closest I got was scrolling without smooth animation.

My question is, is this approach valid, and if so, is there a way to add smooth scrolling in NestedScrollView? Or should I implement this using another approach, e.g. create ListView that contains two items with layout containing TextView and RecyclerView?

ListView

  • List item 1

    • Title 1
    • RecyclerView 1
  • List item 2

    • Title 2
    • RecyclerView2

I believe this approach is not good from performance side of view. Am I right? I just need to find the best practice for this. Thank you.

Answer

Shashwat picture Shashwat · Jun 5, 2018

Please don't use nested scrolling. It will defeat the purpose of recycler view and will keep everything inside the memory as the height will be set to maximum for both the recyclers. Instead proceed with the following two choices:

1.If you don't have a specific background, create a single RecyclerView with adapter similar to the following:

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

    ArrayList<Integer> data = new ArrayList<>();
    private final int VIEW_TYPE_TEXTVIEW = 0;
    private final int VIEW_TYPE_ITEM_1 = 1;
    private final int VIEW_TYPE_ITEM_2 = 2;
    private final LayoutInflater inflater;
    private final ArrayList<Integer> data;

    public MyRecyclerAdapter(Context ctx, ArrayList<Integer> data){
        this.context = ctx;
        this.data = data;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public int getItemViewType(int position) {
        return data.get(position);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if(viewType == VIEW_TYPE_TEXTVIEW){
            View view = inflater.inflate(R.layout.simple_textview, parent, false);
            return new TextViewHolder(view);
        }else if(viewType == VIEW_TYPE_ITEM_1){
            View view = inflater.inflate(R.layout.item_top_recycler, parent, false);
            return new Item1Holder(view);
        }else{
            View view = inflater.inflate(R.layout.item_bottom_recycler, parent, false);
            return new Item2Holder(view);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof TextViewHolder){
            ((TextViewHolder) holder).textView.setText(...);
        }else if(holder instanceof Item1Holder){
            ((Item1Holder) holder).itemTextView.setText(...);
        }else if(holder instanceof Item2Holder){
            ((Item2Holder) holder).itemTextView.setText(...);
        }
    }

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

    class TextViewHolder extends RecyclerView.ViewHolder {


        TextView textView;

        public HeaderHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.tv);
        }
    }
    class Item1Holder extends RecyclerView.ViewHolder {


        TextView itemTextView;

        public HeaderHolder(View itemView) {
            super(itemView);
            itemTextView = itemView.findViewById(R.id.tv);
        }
    }
    class Item2Holder extends RecyclerView.ViewHolder {


        TextView itemTextView;

        public HeaderHolder(View itemView) {
            super(itemView);
            itemTextView = itemView.findViewById(R.id.tv);
        }
    }
}

Then set your adapter like following:

ArrayList<Integer> data = new ArrayList<>();
//Adding first textview
data.add(0);
//Adding 10 elements of first RecyclerView
for(int i = 0; i<10; i++){
    data.add(1);
}
//Adding second textview
data.add(0);
//Adding 10 elements of second RecyclerView
for(int i = 0; i<10; i++){
    data.add(2);
}

adapter = new MyRecyclerAdapter(this, data);
navView.setAdapter(adapter);

This way, you can use the RecyclerView to contain your textview too. This method will give you the best optimization. Make sure that you return the appropriate VIEW_TYPE in getItemViewType() for your upper recyclerView, lower RecyclerView and the TextViews.

The second method is to have one RecyclerView containing 4 items:

  • TextView
  • LinearLayout
  • TextView
  • LinearLayout

Then populate these LinearLayouts with items dynamically. This will ensure that at least one of the Linearlayout is recycled when out of view. Even then, the first approach will be a far better approach than this.