Wie erstelle ich RecyclerView mit mehreren Ansichtstypen?

864

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

Beim Erstellen müssen RecyclerView.Adapterwir angeben, ViewHolderdass die Bindung an den Adapter erfolgt.

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

Ist es also möglich, RecyclerViewmit mehreren Ansichtstypen zu erstellen ?

Pongpat
quelle
5
Zusätzlich zu
ticofab
Überprüfen Sie diesen Link, der auch für Sie nützlich sein kann stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha
1
Gutes Tutorial hier: Guides.codepath.com/android/…
Gene Bo
Überprüfen Sie diesen Link, es ist funktionsfähig stackoverflow.com/questions/39971350/… Wenn es ein Problem gibt, lassen Sie es mich bitte wissen
Ravindra Kushwaha
Die große Bibliothek, um es zu implementieren github.com/vivchar/RendererRecyclerViewAdapter
Vitaly

Antworten:

1269

Ja es ist möglich. Implementieren Sie einfach getItemViewType () und kümmern Sie sich um den viewTypeParameter in onCreateViewHolder().

Also machst du so etwas wie:

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
quelle
3
Das ist mein Punkt, da in einem RecyclerView.Adapter nur ein ViewHolder verfügbar ist. Wie werden Sie mehrere hinzufügen?
Pongpat
48
Dann müssen Sie den Viewholder-Typ in die onBindViewHolder () -Methode umwandeln, was meiner Meinung nach den Zweck des generischen Typs zunichte macht. Übrigens, danke für deine Antwort.
Pongpat
32
Sie können einen BaseHolder erstellen und für alle erforderlichen Typen erweitern. Fügen Sie dann eine abstrakte setData hinzu, die in Implementierungsinhabern überschrieben (überschrieben?) Würde. Auf diese Weise lässt Sie die Sprache Typunterschiede behandeln. Dies funktioniert jedoch nur, wenn Sie über einen einzigen Datensatz verfügen, den alle Listenelemente interpretieren können.
DariusL
2
Was ist mit verschiedenen Layoutdateien? Ich möchte das Layout ändern optionMenuItem. Wie ist es möglich? @ AntonSavin
Pratik Butani
5
Ihre ViewHolders sollten statisch sein, wenn sie sich in Ihrem RecyclerView Adapter
Samer
88

Wenn es nur wenige Layouts für Ansichtstypen gibt und die Bindungslogik einfach ist, befolgen Sie die Lösung von Anton.
Der Code ist jedoch unübersichtlich, wenn Sie die komplexen Layouts und Bindungslogiken verwalten müssen.

Ich glaube, die folgende Lösung ist nützlich für jemanden, der komplexe Ansichtstypen verarbeiten muss.

Basis-DataBinder-Klasse

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

......

}

Die Funktionen, die zum Definieren in dieser Klasse erforderlich sind, entsprechen weitgehend denen der Adapterklasse beim Erstellen des Einzelansichtstyps.
Erstellen Sie für jeden Ansichtstyp die Klasse, indem Sie diesen DataBinder erweitern.

Beispiel für eine DataBinder-Klasse

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

Erstellen Sie eine Adapterklasse, um DataBinder-Klassen zu verwalten.

Basis DataBindAdapter-Klasse

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

......

}

Erstellen Sie die Klasse, indem Sie diese Basisklasse erweitern, und instanziieren Sie dann DataBinder-Klassen und überschreiben Sie abstrakte Methoden

  1. getItemCount Gibt
    die Gesamtzahl der Artikel von DataBinders zurück

  2. getItemViewType
    Definieren Sie die Zuordnungslogik zwischen Adapterposition und Ansichtstyp.

  3. getDataBinder Gibt
    die DataBinder-Instanz basierend auf dem Ansichtstyp zurück

  4. getPosition
    Definieren Sie die Konvertierungslogik von der Position im angegebenen DataBinder in die Adapterposition

  5. getBinderPosition
    Definieren Sie die Konvertierungslogik von der Adapterposition in die Position im DataBinder

Hoffe, diese Lösung wird hilfreich sein.
Ich habe detailliertere Lösungen und Beispiele in GitHub hinterlassen. Bitte verweisen Sie bei Bedarf auf den folgenden Link.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter

yqritc
quelle
4
Ihr Code verwirrt mich leicht. Vielleicht können Sie mir helfen. Ich möchte nicht, dass meine Ansichten durch die Positionen in der Liste definiert werden, sondern durch ihre Ansichtstypen. Es scheint, als ob die Ansichten in Ihrem Code basierend auf ihren Positionen bestimmt werden, d. H. Wenn ich mich also auf Position 1 befinde, wird Ansicht 1 angezeigt, Position 3, Ansicht 3 und alles andere zeigt die Ansicht von Position 2 an. Ich möchte meine Ansichten nicht auf Positionen, sondern auf Ansichtstypen basieren. Wenn ich also spezifiziere, dass der Ansichtstyp Bilder sind, sollte das Bild angezeigt werden. Wie kann ich das machen?
Simon
Entschuldigung, ich konnte Ihre Frage nicht vollständig verstehen ..., aber Sie müssen die Logik irgendwo schreiben, um die Position und den Ansichtstyp zu binden.
yqritc
1
Dieser Code ist nicht verwirrt, dies ist ein RecyclerView-Adaptermuster, und dies sollte als korrekte Antwort auf die Frage ausgenommen werden. Folgen Sie dem Link von @yqritc, verbringen Sie ein wenig Zeit mit der Entdeckung und Sie erhalten ein perfektes Muster für RecyclerView mit verschiedenen Layouts.
Stoycho Andreev
Newbe hier, public class DataBinder<T extends RecyclerView.ViewHolder>kann mir jemand sagen, wie <T someClass>es heißt, damit ich googeln kann, wenn ich den Begriff bekomme. Auch wenn ich sage, abstract public class DataBinder<T extends RecyclerView.ViewHolder>bedeutet das, dass diese Klasse vom Typ ist ViewHolder, so dass im Ergebnis jede Klasse, die diese Klasse erweitert, vom Typ viewHolderist, ist das die Idee?
RGV
1
@cesards du hast mich dazu gebracht, mein Wissen über Polymorphismus wieder aufzufrischen lol .... Java ist nicht schlecht
Paul Okeke
37

Das Folgende ist kein Pseudocode und ich habe es getestet und es hat für mich funktioniert.

Ich wollte eine Kopfansicht in meiner Recyclingansicht erstellen und dann eine Liste mit Bildern unter der Kopfzeile anzeigen, auf die der Benutzer klicken kann.

Ich habe einige Schalter in meinem Code verwendet. Ich weiß nicht, ob dies der effizienteste Weg ist. Geben Sie also Ihre Kommentare ab:

   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
quelle
Das ist schön, was ist, wenn ich mehrere Header an dynamischen Positionen haben möchte? Angenommen, eine Liste von Elementen mit Überschriften, die Kategorien definieren. Ihre Lösung scheint zu erfordern, dass sich die speziellen Header an vorbestimmten int-Positionen befinden.
Bassinator
22

Ja, es ist möglich.

Schreiben Sie einen generischen Ansichtsinhaber:

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

    public abstract  void setDataOnView(int position);
}

Erstellen Sie dann Ihre Ansichtshalter und erweitern Sie den GenericViewHolder. Zum Beispiel dieses:

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

dann sieht die RecyclerView.Adapter-Klasse folgendermaßen aus:

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
quelle
Wie verwende ich dann in einer Aktivität? Sollte der Typ durch die Methode übergeben werden?
Skm
Wie verwende ich diesen Adapter in Aktivität? Und wie erkennt es, welcher Typ in der Liste ist
skm
20

Erstellen Sie unterschiedliche ViewHolder für unterschiedliche Layouts

Geben Sie hier die Bildbeschreibung ein
RecyclerView kann eine beliebige Anzahl von Ansichtsinhabern haben. Zur besseren Lesbarkeit können Sie jedoch sehen, wie Sie einen mit zwei ViewHoldern erstellen.

Dies kann in drei einfachen Schritten erfolgen

  1. Überschreiben public int getItemViewType(int position)
  2. Geben Sie verschiedene ViewHolders basierend auf der ViewType in- onCreateViewHolder()Methode zurück
  3. Füllen Sie die Ansicht basierend auf der onBindViewHolder()Methode itemViewType in

Hier ist ein kleiner Code-Ausschnitt

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) ist der Schlüssel

Meiner Meinung nach ist der Ausgangspunkt für die Erstellung dieser Art von recyclerView das Wissen über diese Methode. Da diese Methode optional überschrieben werden kann, ist sie in der RecylerView-Klasse standardmäßig nicht sichtbar, was wiederum viele Entwickler (einschließlich mich) dazu bringt, sich zu fragen, wo sie anfangen sollen. Sobald Sie wissen, dass diese Methode vorhanden ist, wäre das Erstellen einer solchen RecyclerView ein Kinderspiel.

Sehen wir uns ein Beispiel an, um meinen Standpunkt zu beweisen. Wenn Sie zwei Layouts an alternativen Positionen anzeigen möchten, tun Sie dies

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

Relevante Links:

Schauen Sie sich das Projekt an, in dem ich dies implementiert habe

Rohit Singh
quelle
15

Ja, es ist möglich. In Ihrem Adapter getItemViewType Layout wie folgt ....

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

Als Referenzlink: https://www.journaldev.com/12372/android-recyclerview-example

Sayan Manna
quelle
Ich habe meinen Code neu formatiert, um dieses Snippet nachzuahmen, und es funktioniert jetzt perfekt. Das Problem, das ich hatte, war, dass es beim Streichen über die aktuelle Seite hinaus abstürzen würde. Absturz nicht mehr! Ausgezeichnetes Modell. Vielen Dank . Gut gemacht.
user462990
Konnte tagelang nichts hilfreiches finden, bis ich dieses sah. Danke!
Itay Braha
7

Überlegen Sie sich nach Antons Lösung Folgendes, ViewHolderdas verschiedene Arten von Layouts enthält / verarbeitet / delegiert. Sie sind sich jedoch nicht sicher, ob das Ersetzen eines neuen Layouts funktionieren würde, wenn die Recyclingansicht ViewHoldernicht der Typ der Daten ist, die eingegeben werden.

Wird also grundsätzlich onCreateViewHolder(ViewGroup parent, int viewType)nur aufgerufen, wenn ein neues Ansichtslayout benötigt wird;

getItemViewType(int position)wird für die gerufen viewType;

onBindViewHolder(ViewHolder holder, int position)wird immer aufgerufen, wenn die Ansicht recycelt wird (neue Daten werden eingegeben und versuchen, damit anzuzeigen ViewHolder).

Wenn onBindViewHolderes aufgerufen wird, muss es das richtige Ansichtslayout einfügen und das aktualisieren ViewHolder.

Ist der Weg zum Ersetzen des Ansichtslayouts für das Einbringen korrekt ViewHolderoder ein Problem? Schätzen Sie jeden Kommentar!

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

Bearbeiten: ViewHolder hat das Mitglied mItemViewType, um die Ansicht zu halten

Bearbeiten: sieht so aus, als ob in onBindViewHolder (ViewHolder-Inhaber, int-Position) der übergebene ViewHolder von getItemViewType (int-Position) aufgenommen (oder erstellt) wurde, um sicherzustellen, dass es sich um eine Übereinstimmung handelt, sodass Sie sich dort möglicherweise keine Sorgen um ViewHolder machen müssen Typ stimmt nicht mit dem Datentyp [Position] überein. Weiß jemand mehr darüber, wie der ViewHolder im onBindViewHolder () aufgenommen wird?

Bearbeiten: Sieht aus wie Das Recycling ViewHolderwird nach Typ ausgewählt, also kein Krieger dort.

Bearbeiten: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ beantwortet diese Frage.

Es wird recycelt ViewHolderwie:

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

oder erstellen Sie eine neue, wenn Sie nicht das ViewHolderrichtige Recycling finden .

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

Ich habe eine bessere Lösung, mit der mehrere Ansichtstypen deklarativ und typsicher erstellt werden können. Es ist in Kotlin geschrieben, was übrigens wirklich nett ist.

Einfache Ansichtshalter für alle erforderlichen Ansichtstypen

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
}

Es gibt eine Abstraktion des Adapterdatenelements. Beachten Sie, dass ein Ansichtstyp durch einen Hashcode einer bestimmten Ansichtsinhaberklasse dargestellt wird (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)
   }
}

Nur bindViewHoldermuss in Beton Adapter Elementklassen (Typen sichere Art und Weise) außer Kraft gesetzt werden ,

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

Die Liste solcher AdapterItemMediumObjekte ist eine Datenquelle für den Adapter, die tatsächlich akzeptiert, List<AdapterItem>siehe unten.

Ein wichtiger Teil dieser Lösung ist eine View Holder Factory, die neue Instanzen eines bestimmten ViewHolder bereitstellt

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

Und die einfache Adapterklasse sieht so aus

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

Nur 3 Schritte zum Erstellen eines neuen Ansichtstyps:

  1. Erstellen Sie eine Ansichtshalterklasse
  2. Erstellen Sie eine Adapterelementklasse (erweitert von AdapterItemBase).
  3. Register Ansicht Inhaber Klasse in ViewHolderProvider

Hier ist ein Beispiel für dieses Konzept: android-drawer-template Es geht noch weiter - Ansichtstyp, der als Spinner-Komponente fungiert, auswählbare Adapterelemente.

Michal Faber
quelle
6

Es ist sehr einfach und unkompliziert.

Überschreiben Sie nur getItemViewType () Methode in Ihrem Adapter. Auf Basis der Daten werden unterschiedliche itemViewType-Werte zurückgegeben. Beispiel: Betrachten Sie ein Objekt vom Typ Person mit einem Mitglied isMale. Wenn isMale true ist, geben Sie 1 zurück und isMale ist false, geben Sie 2 in der Methode getItemViewType () zurück .

Kommen wir nun zum createViewHolder (ViewGroup-Eltern, int viewType) , auf der Grundlage eines anderen viewType können Sie die verschiedenen Layoutdateien aufblasen. wie das folgende

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

Überprüfen Sie in onBindViewHolder (VH-Inhaber, int-Position) , wo der Inhaber eine Instanz von AdapterFemaleViewHolderoder AdapterMaleViewHoldervon ist, instanceofund weisen Sie die Werte entsprechend zu.

ViewHolder Kann so sein

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

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

Obwohl die ausgewählte Antwort korrekt ist, möchte ich sie nur weiter ausführen. Ich habe hier einen nützlichen benutzerdefinierten Adapter für mehrere Ansichtstypen in RecyclerView gefunden . Die Kotlin-Version ist da .

Benutzerdefinierter Adapter folgt

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

Ich empfehle diese Bibliothek von Hannes Dorfmann. Es kapselt die gesamte Logik für einen bestimmten Ansichtstyp in einem separaten Objekt namens "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
quelle
1

Eigentlich möchte ich Antons Antwort verbessern .

Da getItemViewType(int position)ein ganzzahliger Wert zurückgegeben wird, können Sie die Layout-Ressourcen-ID zurückgeben, die Sie aufblasen müssen. Auf diese Weise würden Sie eine Logik in der onCreateViewHolder(ViewGroup parent, int viewType)Methode speichern .

Außerdem würde ich nicht empfehlen, intensive Berechnungen durchzuführen, getItemCount()da diese bestimmte Funktion beim Rendern der Liste sowie beim Rendern jedes Elements über die sichtbaren Elemente hinaus mindestens fünfmal aufgerufen wird. Da die notifyDatasetChanged()Methode endgültig ist, können Sie sie leider nicht wirklich überschreiben, aber Sie können sie von einer anderen Funktion im Adapter aus aufrufen.

Dragas
quelle
3
Ja, das könnte funktionieren, wird aber für andere Entwickler verwirrend sein. Auch aus der Dokumentation Note: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.Es ist also besser, etwas mehr Code zu schreiben und keine Hacks zu verwenden.
Ioane Sharvadze
1
Genau. Damals hatte ich diese spezielle Klausel verpasst.
Dragas
Das ist lustig, weil der RecyclerView.Adapter: getItemViewType () hier die Dokumente von developer.android.com/reference/android/support/v7/widget/… vorschlägt, was Dragas gepostet hat. "" anscheinend ohne die Anforderung für getViewTypeCount ()
Deemoe
1

Sie können die Bibliothek verwenden: 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 */

`

Für jedes Element sollten Sie einen ViewRenderer, ViewHolder, SomeModel implementieren:

ViewHolder - ist ein einfacher Ansichtshalter für die Recycler-Ansicht.

SomeModel - es ist Ihr Modell mit ItemModelSchnittstelle

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

Weitere Informationen finden Sie in den Dokumentationen.

Vitaly
quelle
1

Die Implementierung von Ansichtstypen wird mit kotlin einfacher. Hier ist ein Beispiel mit dieser Lichtbibliothek 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
quelle
1

Sie können mehrere RecyclerAdapterViewTypes verarbeiten, indem Sie getItemViewType()den erwarteten viewTypeWert für diese Position zurückgeben

Ich habe eine MultipleViewTypeAdapterMCQ-Liste für Prüfungen erstellt, die möglicherweise eine Frage mit zwei oder mehr gültigen Antworten (Kontrollkästchenoptionen) und eine einzelne Antwortfrage (Radiobutton-Optionen) enthält.

Dafür erhalte ich die Art der Frage aus der API-Antwort und habe diese verwendet, um zu entscheiden, welche Ansicht ich für diese Frage anzeigen muss.

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

    }


}

Sie können das Ausfüllen mehrerer bedingter viewHolder-Daten vermeiden, onBindViewHolder()indem Sie dieselben IDs für ähnliche Ansichten in viewHolders zuweisen, die sich in ihrer Positionierung unterscheiden.

Avinash Ch
quelle
0

Wenn Sie es in Verbindung mit Android Data Binding verwenden möchten, schauen Sie in den https://github.com/evant/binding-collection-adapter - es ist bei weitem die beste Lösung für die verschiedenen Ansichtstypen, die RecyclerViewich je gesehen habe.

Sie können es wie verwenden

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

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

und dann im Layout, wo die Liste ist

 <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}"/>

Ihre Listenelemente müssen eine BaseListItemSchnittstelle implementieren , die so aussieht

interface BaseListItem {
    val layoutRes: Int
}

und die Objektansicht sollte ungefähr so ​​aussehen

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

    <data>

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

   ...

</layout>

Wo YourListItemimplementiertBaseListItem

Hoffe es wird jemandem helfen.

Pavlo Ostasha
quelle
0

Zuerst müssen Sie 2 Layout-XML erstellen. Danach sind im Recyclerview-Adapter TYPE_CALL und TYPE_EMAIL zwei statische Werte mit 1 bzw. 2 in der Adapterklasse.

Definieren Sie jetzt zwei statische Werte auf der Ebene der Adapterklasse der Recycler-Ansicht: private static int TYPE_CALL = 1; private static int TYPE_EMAIL = 2;

Erstellen Sie nun einen Ansichtshalter mit mehreren Ansichten wie folgt:

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

Codieren Sie nun wie folgt in den Methoden onCreateViewHolder und onBindViewHolder im recyclerview-Adapter:

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