1062 Stimmen

Wie man RecyclerView mit mehreren Ansichtstypen erstellt

Von Erstellen von dynamischen Listen mit RecyclerView:

Wenn wir einen RecyclerView.Adapter erstellen, müssen wir einen ViewHolder angeben, der sich mit dem Adapter verbindet.

public class MyAdapter extends RecyclerView.Adapter {

    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 möglich, einen RecyclerView mit mehreren Ansichtstypen zu erstellen?

1514voto

Anton Savin Punkte 39432

Ja, das ist möglich. Implementieren Sie einfach getItemViewType() und achten Sie auf den viewType-Parameter in onCreateViewHolder().

Daher tun Sie etwas Ähnliches wie:

public class MyAdapter extends RecyclerView.Adapter {
    class ViewHolder0 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder0(View itemView){
        ...
        }
    }

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

    @Override
    public int getItemViewType(int position) {
        // Nur als Beispiel, gib 0 oder 2 zurück, abhängig von der Position
        // Beachten Sie, dass im Gegensatz zu ListView-Adaptern die Typen nicht aufeinander folgen müssen
        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;
        }
    }
}

98voto

yqritc Punkte 2041

Wenn die Layouts für Ansichtstypen nur wenige sind und die Bindungslogik einfach ist, folgen Sie Antons Lösung. Aber der Code wird unübersichtlich, wenn Sie komplexe Layouts und Bindungslogik verwalten müssen.

Ich glaube, die folgende Lösung wird für jemanden nützlich sein, der komplexe Ansichtstypen behandeln muss.

Basisklasse DataBinder

abstract public class DataBinder {

    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 in dieser Klasse definiert werden müssen, sind ziemlich ähnlich wie die Adapterklasse beim Erstellen des einzelnen Ansichtstyps.

Erstellen Sie für jeden Ansichtstyp die Klasse, indem Sie diesen DataBinder erweitern.

Beispiel DataBinder-Klasse

public class Sample1Binder extends DataBinder {

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

Um DataBinder-Klassen zu verwalten, erstellen Sie eine Adapterklasse.

Basisklasse DataBindAdapter

abstract public class DataBindAdapter extends RecyclerView.Adapter {

    @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 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
    Geben Sie die Gesamtanzahl der DataBinder-Elemente zurück

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

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

  4. getPosition
    Definieren Sie die Konvertierungslogik zur Adapterposition aus der Position im spezifischen DataBinder

  5. getBinderPosition
    Definieren Sie die Konvertierungslogik zur Position im DataBinder aus der Adapterposition

Ich habe eine ausführlichere Lösung und Beispiele auf GitHub hinterlassen, also schauen Sie bitte bei RecyclerView-MultipleViewTypeAdapter vorbei, wenn Sie sie benötigen.

49voto

Simon Punkte 19110

Das unten stehende ist kein Pseudocode. Ich habe es getestet und es hat für mich funktioniert.

Ich wollte einen Headerview in meinem RecyclerView erstellen und dann eine Liste von Bildern unterhalb des Headers anzeigen, auf die der Benutzer klicken kann.

Ich habe ein paar Schalter in meinem Code verwendet und weiß nicht, ob das der effizienteste Weg ist, das zu tun, also geben Sie gerne Ihre Kommentare:

   public class ViewHolder extends RecyclerView.ViewHolder{

        //Dies sind die allgemeinen Elemente im RecyclerView
        public TextView place;
        public ImageView pics;

        //Dies ist der Header im RecyclerView (viewType = 0)
        public TextView name, description;

        //Dieser Konstruktor würde je nach viewType den zu suchenden View finden
        public ViewHolder(View v, int viewType) {
            super(v);
            if (viewType == 0) {
                name = (TextView) v.findViewById(R.id.name);
                description = (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;
        // erstelle eine neue Ansicht
        switch (viewType) {
            case 0: //Dies wäre die Header-Ansicht in meinem RecyclerView
                v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerview_welcome, parent, false);
                vh = new ViewHolder(v,viewType);
                return  vh;
            default: //Dies wäre die normale Liste mit den Bildern der Orte auf der Welt
                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;
        }
    }

    //Überschrieben, damit ich benutzerdefinierte Zeilen im RecyclerView anzeigen kann
    @Override
    public int getItemViewType(int position) {
        int viewType = 1; //Standardmäßig ist es 1
        if (position == 0) viewType = 0; //Wenn Null, wird es eine Header-Ansicht sein
        return viewType;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //position == 0 bedeutet, dass es sich um die Info-Header-Ansicht im Recycler handelt
        if (position == 0) {
            holder.name.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"Name angeklickt", Toast.LENGTH_SHORT).show();
                }
            });
            holder.description.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"Beschreibung angeklickt", Toast.LENGTH_SHORT).show();
                }
            });
            //Dies bedeutet, dass es jetzt über dem Headerview liegt, da es nicht mehr 0 ist. Zum Testen wechsle ich jetzt zwischen zwei Bildern
        } 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));
            }
        }
    }

40voto

Linh Punkte 49889

Hier ist eine vollständige Beispiel, um einen RecyclerView mit zwei Typen anzuzeigen, der Ansichtstyp wird durch das Objekt entschieden.

Klassenmodell

offene Klasse RecyclerViewItem
Klasse SectionItem(val titel: String) : RecyclerViewItem()
Klasse ContentItem(val name: String, val nummer: Int) : RecyclerViewItem()

Adapter Code

const val VIEW_TYPE_SECTION = 1
const val VIEW_TYPE_ITEM = 2

Klasse UserAdapter : RecyclerView.Adapter() {

    var daten = listOf()

    überschreiben Sie die Funktion getItemViewType(position: Int): Int {
        wenn (daten[position] is SectionItem) {
            return VIEW_TYPE_SECTION
        }
        return VIEW_TYPE_ITEM
    }

    überschreiben Sie die Funktion getItemCount(): Int {
        zurück daten.size
    }

    überschreiben Sie die Funktion onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        wenn (viewType == VIEW_TYPE_SECTION) {
            zurück SectionViewHolder(
                LayoutInflater.from(parent.context).inflate(R.layout.item_user_section, parent, false)
            )
        }
        zurück ContentViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.item_user_content, parent, false)
        )
    }

    überschreiben Sie die Funktion onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val element = daten[position]
        wenn (halter ist SectionViewHolder && element is SectionItem) {
            halter.bind(element)
        }
        wenn (halter ist ContentViewHolder && element is ContentItem) {
            halter.bind(element)
        }
    }

    intern innere Klasse SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        Funktion bind(item: SectionItem) {
            itemView.text_section.text = item.titel
        }
    }

    intern innere Klasse ContentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        Funktion bind(item: ContentItem) {
            itemView.text_name.text = item.name
            itemView.text_number.text = item.nummer.toString()
        }
    }
}

item_user_section.xml

item_user_content.xml

Beispiel Verwendung

val DatenSatz = arrayListOf(
    SectionItem("A1"),
    ContentItem("11", 11),
    ContentItem("12", 12),
    ContentItem("13", 13),

    SectionItem("A2"),
    ContentItem("21", 21),
    ContentItem("22", 22),

    SectionItem("A3"),
    ContentItem("31", 31),
    ContentItem("32", 32),
    ContentItem("33", 33),
    ContentItem("33", 34),
)

recyclerAdapter.daten = DatenSatz
recyclerAdapter.notifyDataSetChanged()

32voto

Rohit Singh Punkte 14318

Erstellen Sie für verschiedene Layouts ein anderes ViewHolder

Geben Sie hier eine Bildbeschreibung ein

Ein RecyclerView kann beliebig viele ViewHolders haben, aber für eine bessere Lesbarkeit zeigen wir Ihnen, wie Sie einen mit zwei ViewHolders erstellen können.

Es kann in drei einfachen Schritten erledigt werden

  1. Überschreiben Sie public int getItemViewType(int position)
  2. Geben Sie verschiedene ViewHolders basierend auf dem ViewType im onCreateViewHolder() -Methode zurück
  3. Populate View basierend auf dem itemViewType in der onBindViewHolder() -Methode

Hier ist ein kleiner Code-Schnipsel:

public class YourListAdapter extends RecyclerView.Adapter {

   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)
      {
            // Typumwandlung des Viewholders
            // Setzen Sie die Eigenschaften des Viewholders
            // Fügen Sie einen Klick-Listener hinzu, falls vorhanden
      }
      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);

        ..... Mach etwas
      }
   }
}

getItemViewType(int position) ist der Schlüssel.

Nach meiner Meinung ist der Ausgangspunkt für die Erstellung dieses Art von RecyclerViews das Wissen über diese Methode. Da diese Methode optional zum Überschreiben ist, ist sie standardmäßig nicht in der RecylerView-Klasse sichtbar, was viele Entwickler (einschließlich mir) dazu bringt, sich zu fragen, wo sie anfangen sollen.

Wenn Sie erst einmal wissen, dass diese Methode existiert, wird die Erstellung eines solchen RecyclerViews ein Kinderspiel sein.

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

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

Relevante Links:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X