RecyclerView with Multiple type<T> of views and how to bind them without casting.

Sree Kumar A.V
3 min readJul 24, 2017

--

Most of the time, we end up having different ViewType for the RecyclerView, Each View may be populated from different Model. In that case how can we avoid casting in onBindViewHolder(RecyclerViewHolder holder, int position)?

Assume you are going to display inventory in RecyclerView. Inventory comes from three different Model(Product,Recipe and Collections). Each Model has corresponding ViewType in RecyclerView.

Lets start with a BaseModel, which has method int getViewType().

public interface BaseModel {
int getViewType();
}

Respective Models implement BaseModel, so product Model will look like, returns the corresponding viewType.

public class Product implements BaseModel{
private int id;
private String title;
private String productGroupId;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getProductGroupId() {
return productGroupId;
}

public void setProductGroupId(String productGroupId) {
this.productGroupId = productGroupId;
}

@Override
public int getViewType() {
return Constants.ViewType.PRODUCT;
}

}

All Inventory items now can be put inside a generic List<? extends BaseModel> inventory.

We can pass this inventory to RecyclerView, but how can we bind to the respective view, without checking for the type in the inventory collection ?

So let’s start by extending the RecyclerView.Holder class and create a BaseViewHolder.

public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder {
public BaseViewHolder(View itemView) {
super(itemView);
}

public abstract void bind(T object);
}

T represent the Type of the object you want to bind to the holder.

One of the holder looks like this, where T represents the Recipe model.

public static class RecipeHolder extends BaseViewHolder<Recipe> {
private TextView mItem;

public RecipeHolder(View itemView) {
super(itemView);
mItem = (TextView) itemView.findViewById(R.id.item);
}

@Override
public void bind(Recipe object) {
mItem.setText(object.getTitle());
}
}

In the onBindViewHolder(BaseViewHolder holder, int position), call the bind() method, No need of any type check and casting. This Technique is called Dynamic Method Dispatch or Runtime Polymorphism.

@Override
@SuppressWarnings("unchecked")
public void onBindViewHolder(BaseViewHolder holder, int position) {
holder.bind(mList.get(position));
}

bind method will call the respective view holder bind method during the Runtime, so you don’t need to do type check and pass the corresponding Model.

Adapter class will look like this.

public class InventoryAdapter extends RecyclerView.Adapter<BaseViewHolder> {

private List<? extends BaseModel> mList;
private LayoutInflater mInflator;

public InventoryAdapter(List<? extends BaseModel> list, Context context) {
this.mList = list;
this.mInflator = LayoutInflater.from(context);
}

public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ViewType.COLLECTION_TYPE:
return new CollectionHolder(mInflator.inflate(R.layout.type_collection,parent,false));
case ViewType.PRODUCT:
return new ProductHolder(mInflator.inflate(R.layout.type_product,parent,false));
case ViewType.RECIPE_TYPE:
return new RecipeHolder(mInflator.inflate(R.layout.type_recipe,parent,false));
}
return null;
}

@Override
@SuppressWarnings("unchecked")
public void onBindViewHolder(BaseViewHolder holder, int position)
{
holder.bind(mList.get(position));
}



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

public int getItemCount() {
return mList.size();
}

public static class CollectionHolder extends BaseViewHolder<Collection> {
private TextView mItem;

public CollectionHolder(View itemView) {
super(itemView);
mItem = (TextView) itemView.findViewById(R.id.item);
}

@Override
public void bind(Collection object) {
mItem.setText(object.getTitle());
}
}
public static class RecipeHolder extends BaseViewHolder<Recipe> {
private TextView mItem;

public RecipeHolder(View itemView) {
super(itemView);
mItem = (TextView) itemView.findViewById(R.id.item);
}

@Override
public void bind(Recipe object) {
mItem.setText(object.getTitle());
}
}


public static class ProductHolder extendsBaseViewHolder<Product> {
private TextView mItem;

public ProductHolder(View itemView) {
super(itemView);
mItem = (TextView) itemView.findViewById(R.id.item);
}

@Override
public void bind(Product object) {
mItem.setText(object.getTitle());
}
}


}

Find the full source code here.

Thanks!! ¯\_(ツ)_/¯

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Sree Kumar A.V
Sree Kumar A.V

Written by Sree Kumar A.V

Lead Android Software Engineer @ YML

Responses (5)

Write a response