A RecyclerView with multiple item types

Ruud Jansen
2 min readJul 7, 2015

--

Update: I wrote a modern Kotlin version of this article.

Last week I had to build a RecyclerView with different types of items. After a few improvement cycles I think I have an elegant solution, that might be interesting for other developers facing a similar use case.

Assume we have a List of items, that we want to insert into our adapter. Since we have different types of items we want to put into the same List, we’ll make an interface for that.

public interface ListItem {
int TYPE_A = 1;
int TYPE_B = 2;

int getListItemType();
}

As you can see, a ListItem can be of two types, in this example we have a class TypeA and TypeB. It would look something like this:

public class TypeA implements ListItem {
private String text;

public TypeA(String text) { this.text = text; }

public String getText() { return text; }

public void setText(String text) { this.text = text; }

@Override
public int getListItemType() {
return ListItem.TYPE_A;
}
}

The implementation of my adapter looks like this:

public class ExampleAdapter
extends RecyclerView.Adapter<ViewHolder> {
private final Context mContext;
private final List<ListItem> mItems;

public ExampleAdapter(Context ctx, List<ListItem> items) {
mContext = ctx;
mItems = items;
}

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

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
View view = null;
switch (type) {
case ListItem.TYPE_A:
view = LayoutInflater
.from(viewGroup.getContext())
.inflate(R.layout.type_a, viewGroup, false);
return new ViewHolderA(view);
case ListItem.TYPE_B:
view = LayoutInflater
.from(viewGroup.getContext())
.inflate(R.layout.type_b, viewGroup, false);
return new ViewHolderB(view);
}
return null;
}

@Override
public void onBindViewHolder(ViewHolder viewHolder, int pos) {
ListItem item = mItems.get(pos);
viewHolder.bindType(item);
}

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

Note that the getItemViewType() method is being implemented to convert the list item position to the list item type (with the possible types being defined in the interface). According to that list item type, the right ViewHolder will be created in the onCreateViewHolder() method. Though, there can only be one onBindViewHolder() implementation, so we have to inherit our view holders from a parent ViewHolder, which only has an empty bindType() method getting called from the adapter.

public abstract class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}

public abstract void bindType(ListItem item) {

}
}

Just like we have a TypeA and TypeB class, we also have a ViewHolderA and a ViewHolderB. A child ViewHolder could look something like this:

public class ViewHolderA extends ViewHolder {
private final TextView mTextView;

public ViewHolderA(View itemView) {
super(itemView);

mTextView = (TextView) itemView.findViewById(R.id.txtView);
}

public void bindType(ListItem item) {
mTextView.setText(((TypeA) item).getText());
}
}

This way we can have different view holders for different data types, using different layouts.

--

--