How to set two adapters to one RecyclerView?

23,250

Solution 1

You cannot set two adapters on one RecyclerView. However you can make a custom Adapter that handles two types of items. Here is how:

Let's assume you need to display objects of two types Animals and Beverages. Let's handle the hardest scenario, that is, your objects require completely different layouts to display them and completely different ViewHolders. Here are the ViewHolders:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    ...
    private static class AnimalViewHolder extends RecyclerView.ViewHolder {
        public AnimalViewHolder(View itemView) {
            super(itemView);

            // prepare your ViewHolder
        }

        public void bind(Animal animal) {
            // display your object
        }
    }

    private static class BeverageViewHolder extends RecyclerView.ViewHolder {
        // like the one above
        ...
    }
}

First you add constants to your adapter representing your view types:

private static final int ITEM_TYPE_ANIMAL;
private static final int ITEM_TYPE_BEVERAGE;

For the sake of simplicity lets assume you store your objects in a list:

private List<Object> items = new ArrayList<>();

public MyAdapter(List<Object> items) {
    this.items.addAll(items);
    ...
}

First you need to implement getItemViewType:

@Override
public int getItemViewType(int position) {
    if (items.get(position) instanceof Animal) {
        return ITEM_TYPE_ANIMAL;
    } else {
        return ITEM_TYPE_BEVERAGE;
    }
} 

Next you use the item type inside onCreateViewHolder:

@Override 
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)  {
    LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());

    if (viewType == ITEM_TYPE_ANIMAL) {
        layoutInflater.inflate(R.layout.item_animal, parent, false);

        return new AnimalViewHolder(view);
    } else {      
        layoutInflater.inflate(R.layout.item_beverage, parent, false);

        return new BeverageViewHolder(view);
    } 
} 

Finally you bind proper view holder:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
    Object item = items.get(position);

    if (viewHolder instanceof AnimalViewHolder) {
        ((AnimalViewHolder) viewHolder).bind((Animal) item);
    } else {
        ((BeverageViewHolder) viewHolder).bind((Beverage) item);
    } 
} 

Solution 2

ConcatAdapter

ConcatAdapter is a new class available in recyclerview:1.2.0-alpha02 which enables you to sequentially combine multiple adapters to be displayed in a single RecyclerView. This enables you to better encapsulate your adapters rather than having to combine many data sources into a single adapter, keeping them focused and re-usable.

Source: https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a

Usage

val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …

val concatAdapter = ConcatAdapter(firstAdapter, secondAdapter, 
     thirdAdapter)
recyclerView.adapter = concatAdapter
Share:
23,250
Hammad Nasir
Author by

Hammad Nasir

Updated on February 05, 2021

Comments

  • Hammad Nasir
    Hammad Nasir over 3 years

    I am developing an android app in which I'm storing two different types of information on 'FirebaseDatabase`.

    Then in the MainActivity, I'm retrieving them and showing to users in a RecyclerView. Both information are meant to be shown in different layouts, i.e., the layouts for both of them are different and that's why I have two make two different Model class and now have 2 different adapters. I'm using FastAdapter by @MikePenz

    So, what I did is I set the adapter on recyclerview in the same sequence as the information is fetched from database:

    1.

    public void prepareDataOfSRequests() {
            gModelClass = new GModelClass(postedBy, ***, ***, ***, "error", ***, formattedTime, currentLatitude, currentLongitude, utcFormatDateTime, userEmail, userID, null, ***, itemID);
            fastItemAdapter.add(0, gModelClass);
            recyclerView.setAdapter(fastItemAdapter);
            recyclerView.smoothScrollToPosition(0);
            emptyRVtext.setVisibility(View.INVISIBLE);
        }
    

    2.

    public void prepareDataOfERequests() {
            eModelClass = new EModelClass(***, ***, ***, ***, "error", ***, formattedTimeE, currentLatitudeE, currentLongitudeE, utcFormatDateTimeE, userEmailE, userIDE, null, ***, ***, ***, ***, itemID);
            fastItemAdapterER.add(eventRequestsModelClass);
            recyclerView.setAdapter(fastItemAdapterER);
            recyclerView.smoothScrollToPosition(0);
            emptyRVtext.setVisibility(View.INVISIBLE);
        }
    

    as the recyclerview is only one and I'm setting 2 different adapters one by one, the recyclerview is getting updated with the 2nd adapter and showing only it's content.

    So, how can I show or set both the adapter to the same 'RecyclerView' and can show the content stored in both the adapters.

  • Alexander Perfilyev
    Alexander Perfilyev over 7 years
    You could just return your layout resource ids inside the getItemViewType . And then in onCreateViewHolder just inflate viewType in the method parameter.
  • Marcin Jedynak
    Marcin Jedynak over 7 years
    You could, but this trick reaches its full potential only if you are using the same ViewHolder for both layouts. Otherwise you stay with if-elses in create and bind and the gain is minimal.
  • Anoop M Maddasseri
    Anoop M Maddasseri over 3 years
    Lol, It's renamed to ConcatAdapter