How to create RecyclerView with multiple view types How to create RecyclerView with multiple view types android android

How to create RecyclerView with multiple view types


Yes, it's possible. Just implement getItemViewType(), and take care of the viewType parameter in onCreateViewHolder().

So you do something like:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    class ViewHolder0 extends RecyclerView.ViewHolder {        ...        public ViewHolder0(View itemView){        ...        }    }    class ViewHolder2 extends RecyclerView.ViewHolder {        ...        public ViewHolder2(View itemView){        ...    }    @Override    public int getItemViewType(int position) {        // Just as an example, return 0 or 2 depending on position        // Note that unlike in ListView adapters, types don't have to be contiguous        return position % 2 * 2;    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {         switch (viewType) {             case 0: return new ViewHolder0(...);             case 2: return new ViewHolder2(...);             ...         }    }    @Override    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {        switch (holder.getItemViewType()) {            case 0:                ViewHolder0 viewHolder0 = (ViewHolder0)holder;                ...                break;            case 2:                ViewHolder2 viewHolder2 = (ViewHolder2)holder;                ...                break;        }    }}


If the layouts for view types are only a few and binding logics are simple, follow Anton's solution. But the code will be messy if you need to manage the complex layouts and binding logics.

I believe the following solution will be useful for someone who need to handle complex view types.

Base DataBinder class

abstract public class DataBinder<T extends RecyclerView.ViewHolder> {    private DataBindAdapter mDataBindAdapter;    public DataBinder(DataBindAdapter dataBindAdapter) {        mDataBindAdapter = dataBindAdapter;    }    abstract public T newViewHolder(ViewGroup parent);    abstract public void bindViewHolder(T holder, int position);    abstract public int getItemCount();......}

The functions needed to define in this class are pretty much same as the adapter class when creating the single view type.

For each view type, create the class by extending this DataBinder.

Sample DataBinder class

public class Sample1Binder extends DataBinder<Sample1Binder.ViewHolder> {    private List<String> mDataSet = new ArrayList();    public Sample1Binder(DataBindAdapter dataBindAdapter) {        super(dataBindAdapter);    }    @Override    public ViewHolder newViewHolder(ViewGroup parent) {        View view = LayoutInflater.from(parent.getContext()).inflate(            R.layout.layout_sample1, parent, false);        return new ViewHolder(view);    }    @Override    public void bindViewHolder(ViewHolder holder, int position) {        String title = mDataSet.get(position);        holder.mTitleText.setText(title);    }    @Override    public int getItemCount() {        return mDataSet.size();    }    public void setDataSet(List<String> dataSet) {        mDataSet.addAll(dataSet);    }    static class ViewHolder extends RecyclerView.ViewHolder {        TextView mTitleText;        public ViewHolder(View view) {            super(view);            mTitleText = (TextView) view.findViewById(R.id.title_type1);        }    }}

In order to manage DataBinder classes, create an adapter class.

Base DataBindAdapter class

abstract public class DataBindAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return getDataBinder(viewType).newViewHolder(parent);    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {        int binderPosition = getBinderPosition(position);        getDataBinder(viewHolder.getItemViewType()).bindViewHolder(viewHolder, binderPosition);    }    @Override    public abstract int getItemCount();    @Override    public abstract int getItemViewType(int position);    public abstract <T extends DataBinder> T getDataBinder(int viewType);    public abstract int getPosition(DataBinder binder, int binderPosition);    public abstract int getBinderPosition(int position);......}

Create the class by extending this base class, and then instantiate DataBinder classes and override abstract methods

  1. getItemCount
    Return the total item count of DataBinders

  2. getItemViewType
    Define the mapping logic between the adapter position and view type.

  3. getDataBinder
    Return the DataBinder instance based on the view type

  4. getPosition
    Define convert logic to the adapter position from the position in the specified DataBinder

  5. getBinderPosition
    Define convert logic to the position in the DataBinder from the adapter position

I left a more detailed solution and samples on GitHub, so please refer to RecyclerView-MultipleViewTypeAdapter if you need.


The below is not pseudocode. I have tested it and it has worked for me.

I wanted to create a headerview in my recyclerview and then display a list of pictures below the header which the user can click on.

I used a few switches in my code and don't know if that is the most efficient way to do this, so feel free to give your comments:

   public class ViewHolder extends RecyclerView.ViewHolder{        //These are the general elements in the RecyclerView        public TextView place;        public ImageView pics;        //This is the Header on the Recycler (viewType = 0)        public TextView name, description;        //This constructor would switch what to findViewBy according to the type of viewType        public ViewHolder(View v, int viewType) {            super(v);            if (viewType == 0) {                name = (TextView) v.findViewById(R.id.name);                decsription = (TextView) v.findViewById(R.id.description);            } else if (viewType == 1) {                place = (TextView) v.findViewById(R.id.place);                pics = (ImageView) v.findViewById(R.id.pics);            }        }    }    @Override    public ViewHolder onCreateViewHolder(ViewGroup parent,                                         int viewType)    {        View v;        ViewHolder vh;        // create a new view        switch (viewType) {            case 0: //This would be the header view in my Recycler                v = LayoutInflater.from(parent.getContext())                    .inflate(R.layout.recyclerview_welcome, parent, false);                vh = new ViewHolder(v,viewType);                return  vh;            default: //This would be the normal list with the pictures of the places in the world                v = LayoutInflater.from(parent.getContext())                        .inflate(R.layout.recyclerview_picture, parent, false);                vh = new ViewHolder(v, viewType);                v.setOnClickListener(new View.OnClickListener(){                    @Override                    public void onClick(View v) {                        Intent intent = new Intent(mContext, nextActivity.class);                        intent.putExtra("ListNo",mRecyclerView.getChildPosition(v));                        mContext.startActivity(intent);                    }                });                return vh;        }    }    //Overridden so that I can display custom rows in the recyclerview    @Override    public int getItemViewType(int position) {        int viewType = 1; //Default is 1        if (position == 0) viewType = 0; //If zero, it will be a header view        return viewType;    }    @Override    public void onBindViewHolder(ViewHolder holder, int position) {        //position == 0 means it's the info header view on the Recycler        if (position == 0) {            holder.name.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    Toast.makeText(mContext,"name clicked", Toast.LENGTH_SHORT).show();                }            });            holder.description.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    Toast.makeText(mContext,"description clicked", Toast.LENGTH_SHORT).show();                }            });            //This means it is beyond the headerview now as it is no longer 0. For testing purposes, I'm alternating between two pics for now        } else if (position > 0) {           holder.place.setText(mDataset[position]);            if (position % 2 == 0) {               holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic1));            }            if (position % 2 == 1) {                holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic2));            }        }    }