Comment créer RecyclerView avec plusieurs types de vues?

865

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

Lorsque nous créons, RecyclerView.Adapternous devons spécifier ce ViewHolderqui sera lié à l'adaptateur.

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

Alors, est-il possible de créer RecyclerViewavec plusieurs types de vues?

Pongpat
la source
5
En plus de la réponse d'Anton, voir ma réponse ici: stackoverflow.com/questions/25914003/…
ticofab
Consultez ces liens qui peuvent également vous être utiles stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha
1
Bon tutoriel ici: guides.codepath.com/android/…
Gene Bo
Vérifiez ces liens, il est réalisable stackoverflow.com/questions/39971350/… S'il y a un problème, veuillez me le faire savoir
Ravindra Kushwaha
La grande bibliothèque pour l'implémenter github.com/vivchar/RendererRecyclerViewAdapter
Vitaly

Réponses:

1270

Oui c'est possible. Implémentez simplement getItemViewType () et prenez soin du viewTypeparamètre dans onCreateViewHolder().

Vous faites donc quelque chose comme:

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;
        }
    }
}
Anton Savin
la source
3
C'est mon point car un seul ViewHolder est disponible dans un RecyclerView.Adapter comment allez-vous en ajouter plusieurs?
Pongpat
48
Ensuite, vous devez convertir le type de viewholder dans la méthode onBindViewHolder () qui, je pense, va à l'encontre du but du type générique. Au fait, merci pour votre réponse.
Pongpat
32
Vous pouvez créer un BaseHolder et l'étendre pour tous les types requis. Ajoutez ensuite un setData abstrait, qui serait remplacé (overrode?) Dans les détenteurs d'implémentation. De cette façon, vous laissez le langage gérer les différences de type. Bien que cela ne fonctionne que si vous disposez d'un seul ensemble de données que tous les éléments de la liste peuvent interpréter.
DariusL
2
Qu'en est-il du fichier de mise en page différent? Je souhaite modifier la mise en page optionMenuItem. Comment c'est possible? @AntonSavin
Pratik Butani
5
Vos ViewHolders doivent être statiques s'ils résident dans votre adaptateur RecyclerView
Samer
88

Si les dispositions pour les types de vue sont rares et que les logiques de liaison sont simples, suivez la solution d'Anton.
Mais le code sera compliqué si vous devez gérer les dispositions complexes et les logiques de liaison.

Je crois que la solution suivante sera utile pour quelqu'un qui doit gérer des types de vues complexes.

Classe DataBinder de base

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();

......

}

Les fonctions nécessaires pour définir dans cette classe sont à peu près les mêmes que la classe d'adaptateur lors de la création du type de vue unique.
Pour chaque type de vue, créez la classe en étendant ce DataBinder.

Exemple de classe DataBinder

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);
        }
    }
}

Afin de gérer les classes DataBinder, créez une classe d'adaptateur.

Classe DataBindAdapter de base

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

......

}

Créez la classe en étendant cette classe de base, puis instanciez les classes DataBinder et remplacez les méthodes abstraites

  1. getItemCount
    Renvoie le nombre total d'éléments de DataBinders

  2. getItemViewType
    Définissez la logique de mappage entre la position de l'adaptateur et le type de vue.

  3. getDataBinder
    Renvoie l'instance DataBinder en fonction du type de vue

  4. getPosition
    Définissez la logique de conversion à la position de l'adaptateur à partir de la position dans le DataBinder spécifié

  5. getBinderPosition
    Définissez la logique de conversion à la position dans le DataBinder à partir de la position de l'adaptateur

J'espère que cette solution vous sera utile.
J'ai laissé plus de détails et de solutions dans GitHub, veuillez donc consulter le lien suivant si vous en avez besoin.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter

yqritc
la source
4
Je suis légèrement confus par votre code, peut-être pouvez-vous m'aider, je ne voudrais pas que mes vues soient définies par les positions dans la liste mais par leurs types de vues. Il semble que les vues de votre code soient déterminées en fonction de leur position, c.-à-d. donc si im sur la position 1, la vue 1 est affichée, la position 3, la vue 3 est affichée et tout le reste affiche la vue de la position 2. Je ne veux pas baser mes vues sur des positions mais sur des types de vues - donc si je spécifie que le type de vue est des images, il devrait afficher l'image. Comment puis je faire ça?
Simon
Désolé, je n'ai pas pu comprendre pleinement votre question ..., mais vous devez écrire la logique quelque part afin de lier la position et le type de vue.
yqritc
1
ce code n'est pas confondu, il s'agit d'un modèle d'adaptateur RecyclerView, et cela doit être excepté comme réponse correcte à la question. Suivez le lien de @yqritc, passez un peu de temps à découvrir et vous aurez un modèle parfait pour RecyclerView avec différents types de mises en page.
Stoycho Andreev
newbe here, public class DataBinder<T extends RecyclerView.ViewHolder>quelqu'un peut-il me dire ce <T someClass>qu'on appelle, donc je peux google si j'obtiens le terme. Aussi quand je dis abstract public class DataBinder<T extends RecyclerView.ViewHolder>cela signifie-t-il que cette classe est de type ViewHolder, donc en résultat chaque classe qui étend cette classe sera de type viewHolderest-ce l'idée?
rgv
1
@cesards tu m'as fait aller rafraîchir mes connaissances sur le polymorphisme lol .... Java n'est pas mauvais
Paul Okeke
37

Ce qui suit n'est pas un pseudocode et je l'ai testé et cela a fonctionné pour moi.

Je voulais créer une vue de tête dans ma vue de recyclage, puis afficher une liste d'images sous l'en-tête sur laquelle l'utilisateur peut cliquer.

J'ai utilisé quelques commutateurs dans mon code, je ne sais pas si c'est le moyen le plus efficace de le faire, alors n'hésitez pas à donner vos commentaires:

   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));
            }

        }
    }
Simon
la source
C'est bien, et si je voulais plusieurs en-têtes à des positions dynamiques? Disons, une liste d'éléments avec des en-têtes définissant des catégories. Votre solution semble exiger que les en-têtes spéciaux soient à des positions int prédéterminées.
Bassinator
22

Oui c'est possible.

Écrivez un support de vue générique:

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

    public abstract  void setDataOnView(int position);
}

puis créez vos supports de vue et faites-les étendre le GenericViewHolder. Par exemple, celui-ci:

     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);
        }
    }
}

alors la classe RecyclerView.Adapter ressemblera à celle-ci:

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);
}
Islam Assi
la source
Comment l'utiliser ensuite dans une activité? Le type doit-il être transmis à la méthode?
skm
Comment utiliser cet adaptateur dans l'activité? Et comment reconnaît-il quel type est dans la liste
skm
20

Créer un ViewHolder différent pour une disposition différente

entrez la description de l'image ici
RecyclerView peut avoir autant de vues que vous le souhaitez, mais pour une meilleure lisibilité, voyons comment en créer une avec deux ViewHolders.

Cela peut se faire en trois étapes simples

  1. Passer outre public int getItemViewType(int position)
  2. Renvoyer différents ViewHolders en fonction de la onCreateViewHolder()méthode ViewType in
  3. Remplir la vue en fonction de la onBindViewHolder()méthode itemViewType dans

Voici un petit extrait de code

public class YourListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

   private static final int LAYOUT_ONE= 0;
   private static final int LAYOUT_TWO= 1;

   @Override
   public int getItemViewType(int position)
   {
      if(position==0)
        return LAYOUT_ONE;
      else
        return LAYOUT_TWO;
   }

   @Override
   public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

      View view =null;
      RecyclerView.ViewHolder viewHolder = null;

      if(viewType==LAYOUT_ONE)
      {
          view = LayoutInflater.from(parent.getContext()).inflate(R.layout.one,parent,false);
          viewHolder = new ViewHolderOne(view);
      }
      else
      {
          view = LayoutInflater.from(parent.getContext()).inflate(R.layout.two,parent,false);
          viewHolder= new ViewHolderTwo(view);
      }

      return viewHolder;
   }

   @Override
   public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {

      if(holder.getItemViewType()== LAYOUT_ONE)
      {
            // Typecast Viewholder 
            // Set Viewholder properties 
            // Add any click listener if any 
      }
      else {

        ViewHolderOne vaultItemHolder = (ViewHolderOne) holder;
        vaultItemHolder.name.setText(displayText);
        vaultItemHolder.name.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
            .......
           }
         });

       }

   }

  //****************  VIEW HOLDER 1 ******************//

   public class ViewHolderOne extends RecyclerView.ViewHolder {

      public TextView name;

      public ViewHolderOne(View itemView) {
         super(itemView);
         name = (TextView)itemView.findViewById(R.id.displayName);
     }
   }


   //****************  VIEW HOLDER 2 ******************//

   public class ViewHolderTwo extends RecyclerView.ViewHolder{

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

        ..... Do something
      }
   }
}

getItemViewType (int position) est la clé

À mon avis, le point de départ pour créer ce type de recyclerView est la connaissance de cette méthode. Étant donné que cette méthode est facultative pour remplacer, elle n'est donc pas visible par défaut dans la classe RecylerView, ce qui fait que de nombreux développeurs (dont moi) se demandent par où commencer. Une fois que vous savez que cette méthode existe, la création d'un tel RecyclerView serait un jeu d'enfant.

Voyons un exemple pour prouver mon point. Si vous souhaitez afficher deux mises en page à des positions alternatives, procédez comme suit

@Override
public int getItemViewType(int position)
{
   if(position%2==0)       // Even position 
     return LAYOUT_ONE;
   else                   // Odd position 
     return LAYOUT_TWO;
}

Liens pertinents:

Découvrez le projet où j'ai mis en œuvre ce

Rohit Singh
la source
15

Oui c'est possible. Dans votre adaptateur getItemViewType Layout comme ceci ....

  public class MultiViewTypeAdapter extends RecyclerView.Adapter {

        private ArrayList<Model>dataSet;
        Context mContext;
        int total_types;
        MediaPlayer mPlayer;
        private boolean fabStateVolume = false;

        public static class TextTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            CardView cardView;

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

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.cardView = (CardView) itemView.findViewById(R.id.card_view);
            }
        }

        public static class ImageTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            ImageView image;

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

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.image = (ImageView) itemView.findViewById(R.id.background);
            }
        }

        public static class AudioTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            FloatingActionButton fab;

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

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.fab = (FloatingActionButton) itemView.findViewById(R.id.fab);
            }
        }

        public MultiViewTypeAdapter(ArrayList<Model>data, Context context) {
            this.dataSet = data;
            this.mContext = context;
            total_types = dataSet.size();
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

            View view;
            switch (viewType) {
                case Model.TEXT_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_type, parent, false);
                    return new TextTypeViewHolder(view);
                case Model.IMAGE_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_type, parent, false);
                    return new ImageTypeViewHolder(view);
                case Model.AUDIO_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_type, parent, false);
                    return new AudioTypeViewHolder(view);
            }
            return null;
        }

        @Override
        public int getItemViewType(int position) {

            switch (dataSet.get(position).type) {
                case 0:
                    return Model.TEXT_TYPE;
                case 1:
                    return Model.IMAGE_TYPE;
                case 2:
                    return Model.AUDIO_TYPE;
                default:
                    return -1;
            }
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) {

            Model object = dataSet.get(listPosition);
            if (object != null) {
                switch (object.type) {
                    case Model.TEXT_TYPE:
                        ((TextTypeViewHolder) holder).txtType.setText(object.text);

                        break;
                    case Model.IMAGE_TYPE:
                        ((ImageTypeViewHolder) holder).txtType.setText(object.text);
                        ((ImageTypeViewHolder) holder).image.setImageResource(object.data);
                        break;
                    case Model.AUDIO_TYPE:

                        ((AudioTypeViewHolder) holder).txtType.setText(object.text);

                }
            }
        }

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

pour le lien de référence: https://www.journaldev.com/12372/android-recyclerview-example

Sayan Manna
la source
J'ai reformaté mon code pour imiter cet extrait et il fonctionne maintenant parfaitement. Le problème que je rencontrais était qu'en glissant au-delà de la page actuelle, il plantait. Crash no More! Excellent modèle ,. Merci . Bien joué.
user462990
Je n'ai rien trouvé d'utile pendant des jours avant d'avoir vu celui-ci, merci!
Itay Braha
7

à la suite de la solution d'Anton, proposez-en une ViewHolderqui contient / gère / délègue différents types de dispositions. Mais je ne sais pas si la nouvelle disposition de remplacement fonctionnerait lorsque la vue de recyclage ViewHoldern'est pas le type de données.

Donc, en gros, onCreateViewHolder(ViewGroup parent, int viewType)n'est appelé que lorsqu'une nouvelle disposition de vue est nécessaire;

getItemViewType(int position)sera appelé pour le viewType;

onBindViewHolder(ViewHolder holder, int position) est toujours appelé lors du recyclage de la vue (de nouvelles données sont introduites et essaient de s'afficher avec ViewHolder ).

Donc, quand onBindViewHolderest appelé, il doit mettre la bonne vue et mettre à jour leViewHolder .

La façon de remplacer la disposition de la vue pour cela ViewHolderdoit-elle être correcte ou un problème? Appréciez tout commentaire!

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);
}

Modifier: ViewHolder a un membre mItemViewType pour contenir la vue

Edit: ressemble à onBindViewHolder (titulaire de ViewHolder, position int) le ViewHolder transmis a été récupéré (ou créé) en regardant getItemViewType (position int) pour vous assurer qu'il s'agit d'une correspondance, donc vous n'aurez peut-être pas à vous inquiéter que ViewHolder soit type ne correspond pas au type de données [position]. Est-ce que quelqu'un sait plus comment le ViewHolder dans onBindViewHolder () est récupéré?

Edit: On dirait que le recyclage ViewHolderest choisi par type, donc pas de guerrier là-bas.

Edit: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ répond à cette question.

Il obtient le recyclage ViewHoldercomme:

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

ou en créer un nouveau si ce n'est pas trouver le recyclage ViewHolderdu bon 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;
}
lannyf
la source
6

J'ai une meilleure solution qui permet de créer plusieurs types de vues de manière déclarative et sécurisée. C'est écrit en Kotlin, ce qui est vraiment sympa.

Porte-vues simples pour tous les types de vue requis

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
}

Il existe une abstraction de l'élément de données de l'adaptateur. Notez qu'un type de vue est représenté par un hashCode d'une classe de détenteur de vue particulière (KClass dans 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)
   }
}

Ne bindViewHolderdoit être remplacé que dans les classes d'éléments d'adaptateur en béton (type de manière sûre)

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() }
    }
}

La liste de ces AdapterItemMediumobjets est une source de données pour l'adaptateur qui accepte réellement List<AdapterItem>voir ci-dessous.

Une partie importante de cette solution est une fabrique de supports de vues qui fournira de nouvelles instances d'un ViewHolder spécifique

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

Et la classe d'adaptateur simple ressemble à ceci

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

Seulement 3 étapes pour créer un nouveau type de vue:

  1. créer une classe de porte-vues
  2. créer une classe d'élément d'adaptateur (s'étendant depuis AdapterItemBase)
  3. enregistrer la classe de titulaire de vue dans ViewHolderProvider

Voici un exemple de ce concept: modèle de tiroir android Il va encore plus loin - voir le type qui agit comme un composant spinner, des éléments d'adaptateur sélectionnables.

Michal Faber
la source
6

C'est très simple et direct.

Remplacez simplement la méthode getItemViewType () dans votre adaptateur. Sur la base des données, renvoyer différentes valeurs itemViewType. Par exemple, considérons un objet de type Personne avec un membre isMale, si isMale est vrai, retourne 1 et isMale est faux, retourne 2 dans la méthode getItemViewType () .

Arrive maintenant au createViewHolder (parent ViewGroup, int viewType) , sur la base de différents viewType, vous pouvez gonfler les différents fichiers de disposition. comme le suivant

 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);
}

dans onBindViewHolder (support VH, position int), vérifiez où le support est une instance de AdapterFemaleViewHolderou AdapterMaleViewHolderparinstanceof et affectez en conséquence les valeurs.

ViewHolder Peut être comme ça

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

    class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
         ...
         public AdapterFemaleViewHolder(View itemView){
            ...
         }
    }
Tulsi
la source
4

Bien que la réponse choisie soit correcte, je veux juste la développer davantage. J'ai trouvé ici un adaptateur personnalisé utile pour plusieurs types de vues dans RecyclerView . Sa version Kotlin est ici .

L'adaptateur personnalisé suit

public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context;
ArrayList<String> list; // ArrayList of your Data Model
final int VIEW_TYPE_ONE = 1;
final int VIEW_TYPE_TWO = 2;

public CustomAdapter(Context context, ArrayList<String> list) { // you can pass other parameters in constructor
    this.context = context;
    this.list = list;
}

private class ViewHolder1 extends RecyclerView.ViewHolder {

    TextView yourView;
    ViewHolder1(final View itemView) {
        super(itemView);
        yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
    }
    void bind(int position) {
        // This method will be called anytime a list item is created or update its data
        //Do your stuff here
        yourView.setText(list.get(position));
    }
}

private class ViewHolder2 extends RecyclerView.ViewHolder {

    TextView yourView;
    ViewHolder2(final View itemView) {
        super(itemView);
        yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
    }
    void bind(int position) {
        // This method will be called anytime a list item is created or update its data
        //Do your stuff here
        yourView.setText(list.get(position));
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   if (viewType == VIEW_TYPE_ONE) {
       return new ViewHolder1(LayoutInflater.from(context).inflate(R.layout.your_list_item_1, parent, false));
   }
   //if its not VIEW_TYPE_ONE then its VIEW_TYPE_TWO
   return new ViewHolder2(LayoutInflater.from(context).inflate(R.layout.your_list_item_2, parent, false));

}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (list.get(position).type == Something) { // put your condition, according to your requirements
        ((ViewHolder1) holder).bind(position);
    } else {
        ((ViewHolder2) holder).bind(position);
    }

}

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

@Override
public int getItemViewType(int position) {
    // here you can get decide from your model's ArrayList, which type of view you need to load. Like
    if (list.get(position).type == Something) { // put your condition, according to your requirements
        return VIEW_TYPE_ONE;
    }
    return VIEW_TYPE_TWO;
}
}
Asad Ali Choudhry
la source
3

Je recommande cette bibliothèque de Hannes Dorfmann. Il encapsule toute la logique liée à un type de vue particulier dans un objet séparé appelé "AdapterDelegate". https://github.com/sockeqwe/AdapterDelegates

public class CatAdapterDelegate extends AdapterDelegate<List<Animal>> {

  private LayoutInflater inflater;

  public CatAdapterDelegate(Activity activity) {
    inflater = activity.getLayoutInflater();
  }

  @Override public boolean isForViewType(@NonNull List<Animal> items, int position) {
    return items.get(position) instanceof Cat;
  }

  @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
    return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
  }

  @Override public void onBindViewHolder(@NonNull List<Animal> items, int position,
      @NonNull RecyclerView.ViewHolder holder, @Nullable List<Object> payloads) {

    CatViewHolder vh = (CatViewHolder) holder;
    Cat cat = (Cat) items.get(position);

    vh.name.setText(cat.getName());
  }

  static class CatViewHolder extends RecyclerView.ViewHolder {

    public TextView name;

    public CatViewHolder(View itemView) {
      super(itemView);
      name = (TextView) itemView.findViewById(R.id.name);
    }
  }
}

public class AnimalAdapter extends ListDelegationAdapter<List<Animal>> {

  public AnimalAdapter(Activity activity, List<Animal> items) {

    // DelegatesManager is a protected Field in ListDelegationAdapter
    delegatesManager.addDelegate(new CatAdapterDelegate(activity))
                    .addDelegate(new DogAdapterDelegate(activity))
                    .addDelegate(new GeckoAdapterDelegate(activity))
                    .addDelegate(23, new SnakeAdapterDelegate(activity));

    // Set the items from super class.
    setItems(items);
  }
}
Dmitrii Bychkov
la source
1

En fait, j'aimerais améliorer la réponse d'Anton .

Puisque getItemViewType(int position)renvoie une valeur entière, vous pouvez renvoyer l'ID de ressource de mise en page dont vous avez besoin pour gonfler. De cette façon, vous enregistrez un peu de logiqueonCreateViewHolder(ViewGroup parent, int viewType) méthode.

Aussi, je ne suggérerais pas de faire des calculs intensifs dans getItemCount() car cette fonction particulière est appelée au moins 5 fois lors du rendu de la liste, ainsi que lors du rendu de chaque élément au-delà des éléments visibles. Malheureusement, puisque la notifyDatasetChanged()méthode est finale, vous ne pouvez pas vraiment la remplacer, mais vous pouvez l'appeler à partir d'une autre fonction de l'adaptateur.

Dragas
la source
3
Oui, cela pourrait fonctionner, mais cela rendra les autres développeurs déroutants. Aussi de la documentation Note: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.Il est donc préférable d'écrire un peu plus de code et de ne pas utiliser de hacks.
Ioane Sharvadze
1
Je suis d'accord. À l'époque, j'avais manqué cette clause particulière.
Dragas
C'est drôle parce que les documents RecyclerView.Adapter: getItemViewType () ici developer.android.com/reference/android/support/v7/widget/… suggèrent ce que Dragas a publié que vous devriez "Envisagez d'utiliser des ressources id pour identifier de manière unique les types de vue d'élément. " apparemment inconscient de l'exigence de getViewTypeCount ()
Deemoe
1

Vous pouvez utiliser la bibliothèque: 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 */

"

Pour chaque élément, vous devez implémenter un ViewRenderer, ViewHolder, SomeModel:

ViewHolder - c'est un simple support de vue de la vue du recycleur.

SomeModel - c'est votre modèle avec ItemModelinterface

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));
 }
}

Pour plus de détails, vous pouvez consulter les documentations.

Vitaly
la source
1

La mise en œuvre des types de vues devient plus facile avec kotlin, voici un exemple avec cette bibliothèque légère https://github.com/Link184/KidAdapter

recyclerView.setUp {
    withViewType {
        withLayoutResId(R.layout.item_int)
        withItems(mutableListOf(1, 2, 3, 4, 5, 6))
        bind<Int> { // this - is adapter view hoder itemView, it - current item
            intName.text = it.toString()
        }
    }


    withViewType("SECOND_STRING_TAG") {
        withLayoutResId(R.layout.item_text)
        withItems(mutableListOf("eight", "nine", "ten", "eleven", "twelve"))
        bind<String> {
            stringName.text = it
        }
    }
}
Link182
la source
1

Vous pouvez traiter plusieurs ViewTypes RecyclerAdapteren faisant getItemViewType()retourner le résultat attenduviewType valeur pour cette position

J'ai préparé une MultipleViewTypeAdapterliste pour la construction de QCM pour les examens qui peuvent jeter une question qui peut avoir 2 réponses valides ou plus (options de case à cocher) et une question à réponse unique (options de radiobutton).

Pour cela, je reçois le type de question de la réponse de l'API et je l'ai utilisé pour décider quelle vue je dois montrer pour cette question.

public class MultiViewTypeAdapter extends RecyclerView.Adapter {

    Context mContext;
    ArrayList<Question> dataSet;
    ArrayList<String> questions;
    private Object radiobuttontype1; 


    //Viewholder to display Questions with checkboxes
    public static class Checkboxtype2 extends RecyclerView.ViewHolder {
        ImageView imgclockcheck;
        CheckBox checkbox;

        public Checkboxtype2(@NonNull View itemView) {
            super(itemView);
            imgclockcheck = (ImageView) itemView.findViewById(R.id.clockout_cbox_image);
            checkbox = (CheckBox) itemView.findViewById(R.id.clockout_cbox);


        }
    }

        //Viewholder to display Questions with radiobuttons

    public static class Radiobuttontype1 extends RecyclerView.ViewHolder {
        ImageView clockout_imageradiobutton;
        RadioButton clockout_radiobutton;
        TextView sample;

        public radiobuttontype1(View itemView) {
            super(itemView);
            clockout_imageradiobutton = (ImageView) itemView.findViewById(R.id.clockout_imageradiobutton);
            clockout_radiobutton = (RadioButton) itemView.findViewById(R.id.clockout_radiobutton);
            sample = (TextView) itemView.findViewById(R.id.sample);
        }
    }

    public MultiViewTypeAdapter(ArrayList<QueDatum> data, Context context) {
        this.dataSet = data;
        this.mContext = context;

    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {

        if (viewType.equalsIgnoreCase("1")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("2")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_cbox_list_row, viewGroup, false);
            view.setHorizontalFadingEdgeEnabled(true);
            return new Checkboxtype2(view);

        } else if (viewType.equalsIgnoreCase("3")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("4")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("5")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);
        }


        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int viewType) {
        if (viewType.equalsIgnoreCase("1")) {
            options =  dataSet.get(i).getOptions();
            question = dataSet.get(i).getQuestion();
            image = options.get(i).getValue();
            ((radiobuttontype1) viewHolder).clockout_radiobutton.setChecked(false);
            ((radiobuttontype1) viewHolder).sample.setText(question);
            //loading image bitmap in the ViewHolder's View
            Picasso.with(mContext)
                    .load(image)
                    .into(((radiobuttontype1) viewHolder).clockout_imageradiobutton);

        } else if (viewType.equalsIgnoreCase("2")) {
            options = (ArrayList<Clockout_questions_Option>) dataSet.get(i).getOptions();
            question = dataSet.get(i).getQuestion();
            image = options.get(i).getValue();
            //loading image bitmap in the ViewHolder's View
            Picasso.with(mContext)
                    .load(image)
                    .into(((Checkboxtype2) viewHolder).imgclockcheck);

        } else if (viewType.equalsIgnoreCase("3")) {
                //fit data to viewHolder for ViewType 3
        } else if (viewType.equalsIgnoreCase("4")) {
//fit data to viewHolder for ViewType 4   
        } else if (viewType.equalsIgnoreCase("5")) {
//fit data to viewHolder for ViewType 5     
        }
    }

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

    /**
     * returns viewType for that position by picking the viewType value from the 
     *     dataset
     */
    @Override
    public int getItemViewType(int position) {
        return dataSet.get(position).getViewType();

    }


}

Vous pouvez éviter plusieurs remplissages de données viewHolder conditionnels en onBindViewHolder()affectant les mêmes identifiants pour les vues similaires parmi les viewHolders qui diffèrent dans leur positionnement.

Avinash Ch
la source
0

Si vous souhaitez l'utiliser en conjonction avec Android Data Binding, regardez dans https://github.com/evant/binding-collection-adapter - c'est de loin la meilleure solution pour les types de vues multiplesRecyclerView j'ai même vus.

vous pouvez l'utiliser comme

var items: AsyncDiffPagedObservableList<BaseListItem> =
        AsyncDiffPagedObservableList(GenericDiff)

    val onItemBind: OnItemBind<BaseListItem> =
        OnItemBind { itemBinding, _, item -> itemBinding.set(BR.item, item.layoutRes) }

puis dans la mise en page où la liste est

 <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                app:enableAnimations="@{false}"
                app:scrollToPosition="@{viewModel.scrollPosition}"

                app:itemBinding="@{viewModel.onItemBind}"
                app:items="@{viewModel.items}"

                app:reverseLayoutManager="@{true}"/>

vos éléments de liste doivent implémenter une BaseListIteminterface qui ressemble à ceci

interface BaseListItem {
    val layoutRes: Int
}

et la vue des éléments devrait ressembler à ceci

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
                name="item"
                type="...presentation.somescreen.list.YourListItem"/>
    </data>

   ...

</layout>

YourListItemmet en œuvreBaseListItem

J'espère que cela aidera quelqu'un.

Pavlo Ostasha
la source
0

vous devez d'abord créer 2 xml de mise en page. après cela, à l'intérieur de l'adaptateur de vue de recyclage, TYPE_CALL et TYPE_EMAIL sont deux valeurs statiques avec respectivement 1 et 2 dans la classe d'adaptateur.

maintenant Définissez deux valeurs statiques au niveau de la classe Adaptateur de vue Recycler par exemple: private static int TYPE_CALL = 1; int statique statique TYPE_EMAIL = 2;

Créez maintenant un support de vue avec plusieurs vues comme ceci:

class CallViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    CallViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}
class EmailViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    EmailViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}

Maintenant, codez comme ci-dessous dans la méthode onCreateViewHolder et onBindViewHolder dans l'adaptateur de recyclage:

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
    View view;
    if (viewType == TYPE_CALL) { // for call layout
        view = LayoutInflater.from(context).inflate(R.layout.item_call, viewGroup, false);
        return new CallViewHolder(view);

    } else { // for email layout
        view = LayoutInflater.from(context).inflate(R.layout.item_email, viewGroup, false);
        return new EmailViewHolder(view);
    }
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
    if (getItemViewType(position) == TYPE_CALL) {
        ((CallViewHolder) viewHolder).setCallDetails(employees.get(position));
    } else {
        ((EmailViewHolder) viewHolder).setEmailDetails(employees.get(position));
    }
}
Genius8020
la source