How to create RecyclerView with multiple view type?

From https://developer.android.com/preview/material/ui-widgets.html

When we creating RecyclerView.Adapter we have to specify ViewHolder that will bind with that adapter.

  • Get bitmap from layout
  • Custom rotated EditText view with working selection, cursor location, etc
  • How do you use Android's real time speech to text?
  • Nested Fragments and The Back Stack
  • How to get the Uri of a image stored on the SDCARD?
  • Dynamic Broadcast Receiver in Fragment
  • 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;
        }
    }
    

    So, is it possible to create RecyclerView with multiple view type?

    Related posts:

    How to use CompositeDisposable of RxJava 2?
    How to implement/use dynamic type in Android&#039;s material design
    Actionbar spinner customisation
    Circular gradient in android
    Best Way to Refresh Adapter/ListView on Android
    WebView Rendering Issue in Android KitKat
  • Mobile web driving directions from current location
  • Task queue on Android like in GCD on iOS?
  • Why is `hasEnrolledFingerprints` giving error that it requires a permission only in my Fragment but not in the Activity in Google's Example?
  • create listview with alphabetic separators android - how?
  • Android Resources converting to string TypedValue warning
  • Android context.getResources.updateConfiguration() deprecated
  • 10 Solutions collect form web for “How to create RecyclerView with multiple view type?”

    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 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

    Hope this solution will be helpful.
    I left more detail solution and samples in GitHub, so please refer the following link if you need.
    https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter

    The below is not pseudocode and 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, 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;
            }
        }
    
        //Overriden 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 its 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));
                }
    
            }
        }
    

    Yes, it is possible.

    Write a generic view holder:

        public abstract class GenericViewHolder extends RecyclerView.ViewHolder
    {
        public GenericViewHolder(View itemView) {
            super(itemView);
        }
    
        public abstract  void setDataOnView(int position);
    }
    

    then create your view holders and make them extend the GenericViewHolder. For example, this one:

         public class SectionViewHolder extends GenericViewHolder{
        public final View mView;
        public final TextView dividerTxtV;
    
        public SectionViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
            dividerTxtV = (TextView) mView.findViewById(R.id.dividerTxtV);
        }
    
        @Override
        public void setDataOnView(int position) {
            try {
                String title= sections.get(position);
                if(title!= null)
                    this.dividerTxtV.setText(title);
            }catch (Exception e){
                new CustomError("Error!"+e.getMessage(), null, false, null, e);
            }
        }
    }
    

    then the RecyclerView.Adapter class will look like this one:

    public class MyClassRecyclerViewAdapter extends RecyclerView.Adapter<MyClassRecyclerViewAdapter.GenericViewHolder> {
    
    @Override
    public int getItemViewType(int position) {
         // depends on your problem
         switch (position) {
             case : return VIEW_TYPE1;
             case : return VIEW_TYPE2;
             ...
         }
    }
    
        @Override
       public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType)  {
        View view;
        if(viewType == VIEW_TYPE1){
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout1, parent, false);
            return new SectionViewHolder(view);
        }else if( viewType == VIEW_TYPE2){
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout2, parent, false);
            return new OtherViewHolder(view);
        }
        // Cont. other view holders ...
        return null;
       }
    
    @Override
    public void onBindViewHolder(GenericViewHolder holder, int position) {
        holder.setDataOnView(position);
    }
    

    following Anton’s solution, come up with this ViewHolder which holds/handles/delegates different type of layouts.
    But not sure if the replacing new layout would work when the recycling view’s ViewHolder is not type of the data roll in.

    So basically,
    onCreateViewHolder(ViewGroup parent, int viewType) is only called when new view layout is needed;

    getItemViewType(int position) will be called for the viewType;

    onBindViewHolder(ViewHolder holder, int position) is always called when recycling the view (new data is brought in and try to display with that ViewHolder).

    So when onBindViewHolder is called it needs to put in the right view layout and update the ViewHolder.

    Is the way correct to replacing the view layout for that ViewHolder to be brought in, or any problem?
    Appreciate any comment!

    public int getItemViewType(int position) {
        TypedData data = mDataSource.get(position);
        return data.type;
    }
    
    public ViewHolder onCreateViewHolder(ViewGroup parent, 
        int viewType) {
        return ViewHolder.makeViewHolder(parent, viewType);
    }
    
    public void onBindViewHolder(ViewHolder holder, 
        int position) {
        TypedData data = mDataSource.get(position);
        holder.updateData(data);
    }
    
    ///
    public static class ViewHolder extends 
        RecyclerView.ViewHolder {
    
        ViewGroup mParentViewGroup;
        View mCurrentViewThisViewHolderIsFor;
        int mDataType;
    
        public TypeOneViewHolder mTypeOneViewHolder;
        public TypeTwoViewHolder mTypeTwoViewHolder;
    
        static ViewHolder makeViewHolder(ViewGroup vwGrp, 
            int dataType) {
            View v = getLayoutView(vwGrp, dataType);
            return new ViewHolder(vwGrp, v, viewType);
        }
    
        static View getLayoutView(ViewGroup vwGrp, 
            int dataType) {
            int layoutId = getLayoutId(dataType);
            return LayoutInflater.from(vwGrp.getContext())
                                 .inflate(layoutId, null);
        }
    
        static int getLayoutId(int dataType) {
            if (dataType == TYPE_ONE) {
                return R.layout.type_one_layout;
            } else if (dataType == TYPE_TWO) {
                return R.layout.type_two_layout;
            }
        }
    
        public ViewHolder(ViewGroup vwGrp, View v, 
            int dataType) {
            super(v);
            mDataType = dataType;
            mParentViewGroup = vwGrp;
            mCurrentViewThisViewHolderIsFor = v;
    
            if (data.type == TYPE_ONE) {
                mTypeOneViewHolder = new TypeOneViewHolder(v);
            } else if (data.type == TYPE_TWO) {
                mTypeTwoViewHolder = new TypeTwoViewHolder(v);
            }
        }
    
        public void updateData(TypeData data) {
            mDataType = data.type;
            if (data.type == TYPE_ONE) {
                mTypeTwoViewHolder = null;
                if (mTypeOneViewHolder == null) {
                    View newView = getLayoutView(mParentViewGroup,
                                   data.type);
    
                    /**
                     *  how to replace new view with 
                        the view in the parent 
                        view container ???
                     */
                    replaceView(mCurrentViewThisViewHolderIsFor, 
                                newView);
                    mCurrentViewThisViewHolderIsFor = newView;
    
                    mTypeOneViewHolder = 
                        new TypeOneViewHolder(newView);
                }
                mTypeOneViewHolder.updateDataTypeOne(data);
    
            } else if (data.type == TYPE_TWO){
                mTypeOneViewHolder = null;
                if (mTypeTwoViewHolder == null) {
                    View newView = getLayoutView(mParentViewGroup, 
                                   data.type);
    
                    /**
                     *  how to replace new view with 
                        the view in the parent view 
                        container ???
                     */
                    replaceView(mCurrentViewThisViewHolderIsFor, 
                                newView);
                    mCurrentViewThisViewHolderIsFor = newView;
    
                    mTypeTwoViewHolder = 
                        new TypeTwoViewHolder(newView);
                }
                mTypeTwoViewHolder.updateDataTypeOne(data);
            }
        }
    }
    
    public static void replaceView(View currentView, 
        View newView) {
        ViewGroup parent = (ViewGroup)currentView.getParent();
        if(parent == null) {
            return;
        }
        final int index = parent.indexOfChild(currentView);
        parent.removeView(currentView);
        parent.addView(newView, index);
    }
    

    Edit: ViewHolder has member mItemViewType to hold the view

    Edit: looks like in onBindViewHolder(ViewHolder holder, int position) the ViewHolder passed in has been picked up (or created) by looked at getItemViewType(int position) to make sure it is a match, so may not need to worry there that ViewHolder’s type does not match the data[position]’s type.
    Does anyone knows more how the ViewHolder in the onBindViewHolder() is picked up?

    Edit: Looks like The recycle ViewHolder is picked by type, so no warrior there.

    Edit: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ answers this question.

    It gets the recycle ViewHolder like:

    holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));
    

    or create new one if not find recycle ViewHolder of right type.

    public ViewHolder getRecycledView(int viewType) {
            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
            if (scrapHeap != null && !scrapHeap.isEmpty()) {
                final int index = scrapHeap.size() - 1;
                final ViewHolder scrap = scrapHeap.get(index);
                scrapHeap.remove(index);
                return scrap;
            }
            return null;
        }
    
    View getViewForPosition(int position, boolean dryRun) {
        ......
    
        if (holder == null) {
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                            + "position " + position + "(offset:" + offsetPosition + ")."
                            + "state:" + mState.getItemCount());
                }
    
                final int type = mAdapter.getItemViewType(offsetPosition);
                // 2) Find from scrap via stable ids, if exists
                if (mAdapter.hasStableIds()) {
                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
                    if (holder != null) {
                        // update position
                        holder.mPosition = offsetPosition;
                        fromScrap = true;
                    }
                }
                if (holder == null && mViewCacheExtension != null) {
                    // We are NOT sending the offsetPosition because LayoutManager does not
                    // know it.
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                        if (holder == null) {
                            throw new IllegalArgumentException("getViewForPositionAndType returned"
                                    + " a view which does not have a ViewHolder");
                        } else if (holder.shouldIgnore()) {
                            throw new IllegalArgumentException("getViewForPositionAndType returned"
                                    + " a view that is ignored. You must call stopIgnoring before"
                                    + " returning this view.");
                        }
                    }
                }
                if (holder == null) { // fallback to recycler
                    // try recycler.
                    // Head to the shared pool.
                    if (DEBUG) {
                        Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
                                + "pool");
                    }
                    holder = getRecycledViewPool()
                            .getRecycledView(mAdapter.getItemViewType(offsetPosition));
                    if (holder != null) {
                        holder.resetInternal();
                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
                            invalidateDisplayListInt(holder);
                        }
                    }
                }
                if (holder == null) {
                    holder = mAdapter.createViewHolder(RecyclerView.this,
                            mAdapter.getItemViewType(offsetPosition));
                    if (DEBUG) {
                        Log.d(TAG, "getViewForPosition created new ViewHolder");
                    }
                }
            }
            boolean bound = false;
            if (mState.isPreLayout() && holder.isBound()) {
                // do not update unless we absolutely have to.
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                if (DEBUG && holder.isRemoved()) {
                    throw new IllegalStateException("Removed holder should be bound and it should"
                            + " come here only in pre-layout. Holder: " + holder);
                }
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                mAdapter.bindViewHolder(holder, offsetPosition);
                attachAccessibilityDelegate(holder.itemView);
                bound = true;
                if (mState.isPreLayout()) {
                    holder.mPreLayoutPosition = position;
                }
            }
    
            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            final LayoutParams rvLayoutParams;
            if (lp == null) {
                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else if (!checkLayoutParams(lp)) {
                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else {
                rvLayoutParams = (LayoutParams) lp;
            }
            rvLayoutParams.mViewHolder = holder;
            rvLayoutParams.mPendingInvalidate = fromScrap && bound;
            return holder.itemView;
    }
    

    Actually, I’d like to improve on Anton’s answer.

    Since getItemViewType(int position) returns an integer value, you can return the layout resource ID you’d need to inflate. That way you’d save some logic in onCreateViewHolder(ViewGroup parent, int viewType) method.

    Also, I wouldn’t suggest doing intensive calculations in getItemCount() as that particular function is called at least 5 times while rendering the list, as well as while rendering each item beyond the visible items. Sadly since notifyDatasetChanged() method is final, you can’t really override it, but you can call it from another function within the adapter.

    I have a better solution which allows to create multiple view types in a declarative and type safe way. It’s written in Kotlin which btw is really nice.

    Simple view holders for all required view types

    class ViewHolderMedium(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val icon: ImageView = itemView.findViewById(R.id.icon) as ImageView
        val label: TextView = itemView.findViewById(R.id.label) as TextView
    }
    

    There is an abstraction of adapter data item. Note that a view type is represented by a hashCode of particular view holder class (KClass in Kotlin)

    trait AdapterItem {
       val viewType: Int
       fun bindViewHolder(viewHolder: RecyclerView.ViewHolder)
    }
    
    abstract class AdapterItemBase<T>(val viewHolderClass: KClass<T>) : AdapterItem {
       override val viewType: Int = viewHolderClass.hashCode()  
       abstract fun bindViewHolder(viewHolder: T)
       override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
           bindViewHolder(viewHolder as T)
       }
    }
    

    Only bindViewHolder needs to be overriden in concrete adapter item classes (type safe way)

    class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase<ViewHolderMedium>(ViewHolderMedium::class) {
        override fun bindViewHolder(viewHolder: ViewHolderMedium) {
            viewHolder.icon.setImageDrawable(icon)
            viewHolder.label.setText(label)
            viewHolder.itemView.setOnClickListener { onClick() }
        }
    }
    

    List of such AdapterItemMedium objects is a data source for the adapter which actually accepts List<AdapterItem> see below.

    Important part of this solution is a view holder factory which will provide fresh instances of a specific ViewHolder

    class ViewHolderProvider {
        private val viewHolderFactories = hashMapOf<Int, Pair<Int, Any>>()
    
        fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            val (layoutId: Int, f: Any) = viewHolderFactories.get(viewType)
            val viewHolderFactory = f as (View) -> RecyclerView.ViewHolder
            val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
            return viewHolderFactory(view)
        }
    
        fun registerViewHolderFactory<T>(key: KClass<T>, layoutId: Int, viewHolderFactory: (View) -> T) {
            viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory))
        }
    }
    

    And the simple adapter class looks like this

    public class MultitypeAdapter(val items: List<AdapterItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
       val viewHolderProvider = ViewHolderProvider() // inject ex Dagger2
    
       init {
            viewHolderProvider!!.registerViewHolderFactory(ViewHolderMedium::class, R.layout.item_medium, { itemView ->
                ViewHolderMedium(itemView)
            })
       }
    
       override fun getItemViewType(position: Int): Int {
            return items[position].viewType
        }
    
        override fun getItemCount(): Int {
            return items.size()
        }
    
        override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
            return viewHolderProvider!!.provideViewHolder(viewGroup, viewType)
        }
    
        override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
            items[position].bindViewHolder(viewHolder)     
        }
    }
    

    Only 3 steps to create a new view type:

    1. create a view holder class
    2. create an adapter item class (extending from AdapterItemBase)
    3. register view holder class in ViewHolderProvider

    Here is an example of this concept : android-drawer-template
    It goes even further – view type which act as a spinner component, selectable adapter items.

    It is very simple and straight forward.

    Just Override getItemViewType() method in your adapter. On the basis of data return different itemViewType values. e.g
    Consider an object of type Person with a member isMale, if isMale is true, return 1 and isMale is false, return 2 in getItemViewType() method.

    Now comes to the createViewHolder (ViewGroup parent, int viewType), on the basis of different viewType yon can inflate the different layout file. like the following

     if (viewType ==1){
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.male,parent,false);
        return new AdapterMaleViewHolder(view);
    }
    else{
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.female,parent,false);
        return new AdapterFemaleViewHolder(view);
    }
    

    in onBindViewHolder (VH holder,int position) check where holder is instance of AdapterFemaleViewHolder or AdapterMaleViewHolder by instanceof and accordingly assign the values.

    ViewHolder May be like this

        class AdapterMaleViewHolder extends RecyclerView.ViewHolder {
                ...
                public AdapterMaleViewHolder(View itemView){
                ...
                }
            }
    
        class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
             ...
             public AdapterFemaleViewHolder(View itemView){
                ...
             }
        }
    

    You can use the library: https://github.com/vivchar/RendererRecyclerViewAdapter

    mRecyclerViewAdapter = new RendererRecyclerViewAdapter(); /* included from library */
    mRecyclerViewAdapter.registerRenderer(new SomeViewRenderer(SomeModel.TYPE, this));
    mRecyclerViewAdapter.registerRenderer(...); /* you can use several types of cells */
    

    `

    For each item, you should to implement a ViewRenderer, ViewHolder, SomeModel:

    ViewHolder – it is a simple view holder of recycler view.

    SomeModel – it is your model with ItemModel interface

    public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> {
    
      public SomeViewRenderer(final int type, final Context context) {
        super(type, context);
      }
      @Override
     public void bindView(@NonNull final SomeModel model, @NonNull final SomeViewHolder holder) {
        holder.mTitle.setText(model.getTitle());
     }
     @NonNull
     @Override
     public SomeViewHolder createViewHolder(@Nullable final ViewGroup parent) {
        return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false));
     }
    }
    

    For more details you can look documentations.

    There Too LAzy Solution are presented…
    Try this out very easy and quick solution to make multiple layout in Recycler view

    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
        if(ChatFragment.isMe) {  // isMe is boolean static field in ChatFragment class
            View view = inflater.inflate(R.layout.my_chat_layout, parent, false);
            MyViewHolder holder = new MyViewHolder(view);
            return holder;
        }
        else{
            View view = inflater.inflate(R.layout.other_chat_layout, parent, false);
            MyViewHolder holder = new MyViewHolder(view);
            return holder;
        }
    
    }` 
    

    use this when you need to change layout

    public static Boolean isMe;
    

    change isME( true or false ) is upto you

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.