ConcatAdapter takes multiple recycler view adapters and merges them together to show with single RecyclerView.

This article demonstrates on implementing ConcatAdapter in android application. I have created a useful example, to use this feature in your app. So Let’s get into it and you can check the video below what it looks like.

You have to change your RecyclerView dependency from v1.1.0 to v1.2.0-alpha04. This class comes with upgrading to newer dependency which is still in alpha release. You can check for new features and fixes available from official Android Developer Page.

implementation "androidx.recyclerview:recyclerview:1.2.0-alpha04"

Note: As of alpha02 update, earlier this has renamed to ConcatAdapter from MergeAdapter.

1. Create ConcatAdapter

Implementing a RecyclerView with multiple Adapters using ConcatAdapter:

// Get reference to created RecyclerView layout.
RecyclerView recyclerView = findViewById(R.id.recycler_view);

// First Adapter
AnimalAdapter animalAdapter = new AnimalAdapter(animalList);
// Second Adapter
PlanetAdapter planetAdapter = new PlanetAdapter(planetList);

// Create a new ConcatAdapter and pass created adapters in sequence we need to show.
ConcatAdapter concatAdapter = new ConcatAdapter(animalAdapter, planetAdapter);
// Attach adapter to recyclerView.
recyclerView.setAdapter(concatAdapter);

2. Removing a Adapter

You can remove any adapter from previously added to ConcatAdapter in runtime.

concatAdapter.removeAdapter(animalAdapter);

This could be helpful when there is no items in the list to display or depending on use-cases.

3. Adding a Adapter

Similarly you can add adapter to ConcatAdapter at run-time.

concatAdapter.addAdapter(animalAdapter);

Conflict between adapters

By this time you might be wondering if adapters share same ViewHolder or similar item id’s. But that is not how it works, If there are 2 number of adapters concatenated they both use different ViewHolders.
You might want to take look at below image which outlines the working.

Which means the item id’s between these adapters will be same and uses different list items to populate the data in adapter.
For example, If the first item inside AnimalAdapter and first item in PlanetAdapter are same, we still don’t face any conflict while working with those id’s. But you have to handle such operations if you they are making use of different models.

4. Passing data from RecyclerView

When you implement ConcatAdapter with RecyclerView you might end up with multiple ViewHolders, Models and List items. So while passing the data between other activities you have to aware of which data needs to be passed.

Assume we are passing data from MainActivity to DetailActivity using intents when user clicks a item in the list.

AnimalAdapter click listener:

Model = Animal.java , ViewHolder = AnimalViewHolder

@Override
public void onAnimalSelected(Animals animals) {
    Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
    intent.putExtra(DetailActivity.INTENT_EXTRA_ANIMAL_DATA, animals);
    startActivity(intent);
}

PlanetAdapter click listener :

Model = Planet.java , ViewHolder = PlanetViewHolder

@Override
public void onPlanetSelected(Planets planets) {
    Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
    intent.putExtra(DetailActivity.INTENT_EXTRA_PLANET_DATA, planets);
    startActivity(intent);
}

5. Receive data in DetailActivity

Intent intent = getIntent();
if (intent != null) {
    if (intent.hasExtra(INTENT_EXTRA_ANIMAL_DATA)) {
        final Animals animals = intent.getParcelableExtra(INTENT_EXTRA_ANIMAL_DATA);
        detailTextView.setText(animals.getAnimalName());
    } else if ((intent.hasExtra(INTENT_EXTRA_PLANET_DATA))) {
        final Planets planets = intent.getParcelableExtra(INTENT_EXTRA_PLANET_DATA);
        detailTextView.setText(planets.getPlanetName());
    }
}

6. Things to remember

  • If you are using ViewPager2 along with this RecyclerView version you have to change the dependency from androidx.viewpager2:viewpager2:1.1.0 to androidx.viewpager2:viewpager2:1.1.0-alpha01 .
    You can check Android Official Release page for newer releases on this version.
  • While using ConcatAdapter if you have to call item position in adapter, use getBindingAdapterPosition() instead of getAdapterPosition() because we are using multiple adapters at once.
    You can check Official Page for more information.

Those were the key features available with the new release.
Now I will add few classes here from example for reference.

AnimalAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class AnimalAdapter extends RecyclerView.Adapter<AnimalAdapter.AnimalViewHolder> {

    private final List<Animals> mAnimalList;
    private final AnimalAdapterListener mListener;

    AnimalAdapter(List<Animals> animalsList, AnimalAdapterListener listener) {
        this.mAnimalList = animalsList;
        this.mListener = listener;
    }

    public interface AnimalAdapterListener {
        void onAnimalSelected(Animals animals);
    }

    static class AnimalViewHolder extends RecyclerView.ViewHolder {

        private final TextView mAnimalNameTextView;

        AnimalViewHolder(@NonNull View itemView) {
            super(itemView);
            mAnimalNameTextView = itemView.findViewById(R.id.animal_name_item_text_view);
        }
    }

    @NonNull
    @Override
    public AnimalViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_animals, parent, false);
        return new AnimalViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull AnimalViewHolder holder, int position) {
        final Animals animals = mAnimalList.get(position);
        holder.mAnimalNameTextView.setText(animals.getAnimalName());

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mListener.onAnimalSelected(animals);
            }
        });
    }

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

PlanetAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class PlanetAdapter extends RecyclerView.Adapter<PlanetAdapter.PlanetViewHolder> {

    private final List<Planets> mPlanetsList;
    private final PlanetAdapterListener mListener;

    PlanetAdapter(List<Planets> planetsList, PlanetAdapterListener listener) {
        this.mPlanetsList = planetsList;
        this.mListener = listener;
    }

    public interface PlanetAdapterListener {
        void onPlanetSelected(Planets planets);
    }

    static class PlanetViewHolder extends RecyclerView.ViewHolder {

        private final TextView mPlanetNameTextView;

        PlanetViewHolder(@NonNull final View itemView) {
            super(itemView);
            mPlanetNameTextView = itemView.findViewById(R.id.planet_name_item_text_view);
        }
    }

    @NonNull
    @Override
    public PlanetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_planets, parent, false);
        return new PlanetViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull PlanetViewHolder holder, int position) {
        final Planets planets = mPlanetsList.get(position);
        holder.mPlanetNameTextView.setText(planets.getPlanetName());

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mListener.onPlanetSelected(planets);
            }
        });
    }

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

MainActivity.java :

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ConcatAdapter;
import androidx.recyclerview.widget.RecyclerView;

import com.developersbreach.concatadapterexample.AnimalAdapter.AnimalAdapterListener;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private List<Animals> mAnimalsList;
    private List<Planets> mPlanetsList;

    private ConcatAdapter mConcatAdapter;
    private AnimalAdapter mAnimalAdapter;
    private PlanetAdapter mPlanetAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final RecyclerView recyclerView = findViewById(R.id.recycler_view);

        addAnimalsToList();
        addPlanetsToList();

        mAnimalAdapter = new AnimalAdapter(mAnimalsList, new AnimalItemClickListener());
        mPlanetAdapter = new PlanetAdapter(mPlanetsList, new PlanetItemClickListener());
        mConcatAdapter = new ConcatAdapter(mAnimalAdapter, mPlanetAdapter);
        recyclerView.setAdapter(mConcatAdapter);
    }

    public void removeAnimalAdapter(View view) {
        mConcatAdapter.removeAdapter(mAnimalAdapter);
    }

    public void addAnimalAdapter(View view) {
        mConcatAdapter.addAdapter(mAnimalAdapter);
    }

    public void removePlanetAdapter(View view) {
        mConcatAdapter.removeAdapter(mPlanetAdapter);
    }

    public void addPlanetAdapter(View view) {
        mConcatAdapter.addAdapter(mPlanetAdapter);
    }

    private class AnimalItemClickListener implements AnimalAdapterListener {
        @Override
        public void onAnimalSelected(Animals animals) {
            Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
            intent.putExtra(DetailActivity.INTENT_EXTRA_ANIMAL_DATA, animals);
            startActivity(intent);
        }
    }

    private class PlanetItemClickListener implements PlanetAdapter.PlanetAdapterListener {
        @Override
        public void onPlanetSelected(Planets planets) {
            Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
            intent.putExtra(DetailActivity.INTENT_EXTRA_PLANET_DATA, planets);
            startActivity(intent);
        }
    }

    private void addAnimalsToList() {
        mAnimalsList = new ArrayList<>();
        mAnimalsList.add(new Animals(1, "Cat"));
        mAnimalsList.add(new Animals(2, "Dog"));
        mAnimalsList.add(new Animals(3, "Giraffe"));
    }

    private void addPlanetsToList() {
        mPlanetsList = new ArrayList<>();
        mPlanetsList.add(new Planets(1, "Mercury"));
        mPlanetsList.add(new Planets(2, "Pluto"));
        mPlanetsList.add(new Planets(3, "Earth"));
    }
}

DetailActivity.java :

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

public class DetailActivity extends AppCompatActivity {

    public static final String INTENT_EXTRA_ANIMAL_DATA = "ANIMAL_DATA";
    public static final String INTENT_EXTRA_PLANET_DATA = "PLANET_DATA";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        TextView detailTextView = findViewById(R.id.detail_text_view);

        Intent intent = getIntent();
        if (intent != null) {
            if (intent.hasExtra(INTENT_EXTRA_ANIMAL_DATA)) {
                final Animals animals = intent.getParcelableExtra(INTENT_EXTRA_ANIMAL_DATA);
                detailTextView.setText(animals.getAnimalName() + "\n" + "Id " + animals.getAnimalId());
            } else if ((intent.hasExtra(INTENT_EXTRA_PLANET_DATA))) {
                final Planets planets = intent.getParcelableExtra(INTENT_EXTRA_PLANET_DATA);
                detailTextView.setText(planets.getPlanetName() + "\n" + "Id " + planets.getPlanetId());
            }
        }
    }
}

You can get remaining two model classes Animals.java , Planet.java and layouts from project link below.

Here We Go Again :]

if (article was helful) {
    println("Like and subscribe to blog below.")
    println("You will receive email for new articles.")
} else {
    println("Let me know what i should blog on.")
}

7. Reference and code

Check for RecyclerView releases
ConcatAdapter Documentation
Article by Developer Advocate with use case

Rajasekhar K E


Hi ! I’m Rajasekhar a Programmer who does Android Development, Creative & Technical writing, Kotlin enthusiast and Engineering graduate. I learn from Open Source and always happy to assist others with my work. I spend most of time Training, Assisting & Mentoring students who are absolute Beginners in android development. I’m also running my startup named Developers Breach which mostly works on contributing to open source.

Here We Go Again : (

if (article == helpful) {
    println("Like and subscribe to blog newsletter.")
} else {
    println("Let me know what i should blog on.")
}

This site uses Akismet to reduce spam. Learn how your comment data is processed.