Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create RecyclerView with multiple view types

From Create dynamic lists with RecyclerView:

When we create a RecyclerView.Adapter we have to specify ViewHolder that will bind with the adapter.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {      private String[] mDataset;      public MyAdapter(String[] myDataset) {         mDataset = myDataset;     }      public static class ViewHolder extends RecyclerView.ViewHolder {         public TextView mTextView;         public ViewHolder(TextView v) {             super(v);             mTextView = v;         }     }      @Override     public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);          //findViewById...          ViewHolder vh = new ViewHolder(v);         return vh;     }      @Override     public void onBindViewHolder(ViewHolder holder, int position) {         holder.mTextView.setText(mDataset[position]);     }      @Override     public int getItemCount() {         return mDataset.length;     } } 

Is it possible to create RecyclerView with multiple view types?

like image 634
Pongpat Avatar asked Oct 07 '14 20:10

Pongpat


People also ask

What is ViewType in onCreateViewHolder?

onCreateViewHolder(parent: ViewGroup, viewType: Int) Parent is the ViewGroup into which the new View will be added after it is bound to an adapter position and viewType is the type of the new View. This method will return a new ViewHolder that holds a View of the given view type.


Video Answer


2 Answers

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;         }     } } 
like image 69
Anton Savin Avatar answered Sep 27 '22 21:09

Anton Savin


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.

like image 29
yqritc Avatar answered Sep 27 '22 20:09

yqritc