Recyclerview e gestione di diversi tipi di inflazione di fila


124

Sto cercando di lavorare con il nuovo RecyclerView, ma non sono riuscito a trovare un esempio di a RecyclerViewcon diversi tipi di righe / visualizzazioni di schede che si gonfiano.

Con ListViewsovrascrivo getViewTypeCounte e getItemViewType, per gestire diversi tipi di righe.

Dovrei farlo nel modo "vecchio" o dovrei fare qualcosa con LayoutManager? Mi chiedevo se qualcuno potesse indicarmi la giusta direzione. Perché posso trovare solo esempi con un tipo.

Voglio avere un elenco di carte leggermente diverse. O dovrei semplicemente usare un scrollViewcon cardViewsal suo interno ... farlo senza l'adattatore e recyclerView?


qual è la differenza tra i tipi di articoli? come dovrebbe reagire il recyclerview a diversi tipi? In generale, non c'è niente che tu possa fare con una scrollview / listview che non puoi fare con una vista recyclerview, ma non viceversa
Gil Moshayof,

In realtà è come quello che vedi nel Google Play Store. Nella parte superiore puoi avere un'intestazione, quindi puoi vedere tre carte, quindi hai una sezione con informazioni. Questo viene fatto in una vista riciclo / lista? O scrollview? Perché se è una vista di scorrimento, devo prima determinare tutti i layout. Con una visualizzazione elenco posso solo aggiungere alcuni oggetti al mio set di dati e il layout giusto verrà gonfiato. Quindi voglio sapere, come fare l'ultima parte con il nuovo Recyclerview, devo scavalcare metodi come listview?
Lokkio,

AnyOne in cerca di demo github per layout a più righe usando il codice
nitesh


Controllare questi link, si realizzabile per voi: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

Risposte:


202

Gestire la logica di righe / sezioni simile a UITableView di iOS non è così semplice in Android come in iOS, tuttavia, quando si utilizza RecyclerView, la flessibilità di ciò che è possibile fare è molto maggiore.

Alla fine, è tutto su come capire quale tipo di vista stai visualizzando nell'adattatore. Una volta capito questo, dovrebbe essere facile navigare (non proprio, ma almeno lo avrai risolto).

L'adapter espone due metodi che è necessario ignorare:

getItemViewType(int position)

L'implementazione predefinita di questo metodo restituirà sempre 0, indicando che esiste solo 1 tipo di vista. Nel tuo caso, non è così, quindi dovrai trovare un modo per affermare quale riga corrisponde a quale tipo di vista. A differenza di iOS, che lo gestisce per te con righe e sezioni, qui avrai un solo indice su cui contare e dovrai usare le tue capacità di sviluppatore per sapere quando una posizione è correlata a un'intestazione di sezione e quando è correlata a una fila normale.

createViewHolder(ViewGroup parent, int viewType)

È necessario ignorare questo metodo comunque, ma di solito le persone ignorano semplicemente il parametro viewType. A seconda del tipo di vista, è necessario gonfiare la risorsa di layout corretta e creare il proprio supporto di visualizzazione di conseguenza. RecyclerView gestirà il riciclaggio di diversi tipi di vista in modo da evitare lo scontro di diversi tipi di vista.

Se hai intenzione di utilizzare un LayoutManager predefinito, ad esempio LinearLayoutManager, dovresti essere pronto per partire. Se stai pensando di realizzare la tua implementazione LayoutManager, dovrai lavorare un po 'di più. L'unica API con cui devi davvero lavorare è quella findViewByPosition(int position)che fornisce una determinata vista in una determinata posizione. Poiché probabilmente vorrai disporlo in modo diverso a seconda del tipo di questa vista, hai alcune opzioni:

  1. Di solito quando si utilizza il modello ViewHolder, si imposta il tag della vista con il supporto della vista. È possibile utilizzarlo durante il runtime nel gestore del layout per scoprire di che tipo è la vista aggiungendo un campo nel supporto della vista che lo esprime.

  2. Dato che avrai bisogno di una funzione che determina quale posizione è correlata a quale tipo di vista, potresti anche rendere questo metodo accessibile in tutto il mondo in qualche modo (forse una classe singleton che gestisce i dati?), E quindi puoi semplicemente interrogare lo stesso metodo in base a la posizione.

Ecco un esempio di codice:

// in this sample, I use an object array to simulate the data of the list. 
// I assume that if the object is a String, it means I should display a header with a basic title.
// If not, I assume it's a custom model object I created which I will use to bind my normal rows.
private Object[] myData;

public static final int ITEM_TYPE_NORMAL = 0;
public static final int ITEM_TYPE_HEADER = 1;

public class MyAdapter extends Adapter<ViewHolder> {

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

        if (viewType == ITEM_TYPE_NORMAL) {
            View normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);
            return new MyNormalViewHolder(normalView); // view holder for normal items
        } else if (viewType == ITEM_TYPE_HEADER) {
            View headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);
            return new MyHeaderViewHolder(headerRow); // view holder for header items
        }
    }


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

        final int itemType = getItemViewType(position);

        if (itemType == ITEM_TYPE_NORMAL) {
            ((MyNormalViewHolder)holder).bindData((MyModel)myData[position]);
        } else if (itemType == ITEM_TYPE_HEADER) {
            ((MyHeaderViewHolder)holder).setHeaderText((String)myData[position]);
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (myData[position] instanceof String) {
            return ITEM_TYPE_HEADER;
        } else {
            return ITEM_TYPE_NORMAL;
        }
    }

    @Override
    public int getItemCount() {
        return myData.length;
    }
}

Ecco un esempio di come dovrebbero apparire questi possessori di vista:

public MyHeaderViewHolder extends ViewHolder {

    private TextView headerLabel;    

    public MyHeaderViewHolder(View view) {
        super(view);

        headerLabel = (TextView)view.findViewById(R.id.headerLabel);
    }

    public void setHeaderText(String text) {
        headerLabel.setText(text);
    }    
}


public MyNormalViewHolder extends ViewHolder {

    private TextView titleLabel;
    private TextView descriptionLabel;    

    public MyNormalViewHolder(View view) {
        super(view);

        titleLabel = (TextView)view.findViewById(R.id.titleLabel);
        descriptionLabel = (TextView)view.findViewById(R.id.descriptionLabel);
    }

    public void bindData(MyModel model) {
        titleLabel.setText(model.getTitle());
        descriptionLabel.setText(model.getDescription());
    }    
}

Naturalmente, questo esempio presuppone che tu abbia costruito la tua fonte di dati (myData) in un modo che semplifichi l'implementazione di un adattatore in questo modo. Ad esempio, ti mostrerò come costruirò un'origine dati che mostra un elenco di nomi e un'intestazione per ogni volta che cambia la 1a lettera del nome (supponiamo che l'elenco sia alfabetico) - simile a come un contatto la lista sarebbe simile a:

// Assume names & descriptions are non-null and have the same length.
// Assume names are alphabetized
private void processDataSource(String[] names, String[] descriptions) {
    String nextFirstLetter = "";
    String currentFirstLetter;

    List<Object> data = new ArrayList<Object>();

    for (int i = 0; i < names.length; i++) {
        currentFirstLetter = names[i].substring(0, 1); // get the 1st letter of the name

        // if the first letter of this name is different from the last one, add a header row
        if (!currentFirstLetter.equals(nextFirstLetter)) {
            nextFirstLetter = currentFirstLetter;
            data.add(nextFirstLetter);
        }

        data.add(new MyModel(names[i], descriptions[i]));
    }

    myData = data.toArray();
}

Questo esempio viene a risolvere un problema abbastanza specifico, ma spero che questo ti dia una buona panoramica su come gestire diversi tipi di riga in un riciclatore e ti permetta di fare gli adattamenti necessari nel tuo codice per soddisfare le tue esigenze.


Abbastanza meraviglioso, e penso che questo sia un ottimo esempio per risolvere questo problema Soluzione campione
Amt87

Un altro esempio per infalting le diverse righe all'interno del recyelview cioè: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

Dovrebbe esserenames[i].substring(0, 1)
Kyle il

1
Inoltre, per le visualizzazioni dei riciclatori con elementi eterogenei, è consigliabile guardare anche SpanSizeLookup. stackoverflow.com/questions/26869312/...
Mahori

È utile. Sulla base di questa risposta, ho anche un'idea per implementare la vista multi tipo in Adapter usando enum. L'enum avrà un metodo onCreateViewHolderche ci aiuta a creare il view viewer. Per maggiori dettagli si consiglia di controllare la mia parte: stackoverflow.com/questions/47245398/...
quangson91

114

Il trucco è creare sottoclassi di ViewHolder e poi lanciarle.

public class GroupViewHolder extends RecyclerView.ViewHolder {
    TextView mTitle;
    TextView mContent;
    public GroupViewHolder(View itemView) {
        super (itemView);
        // init views...
    }
}

public class ImageViewHolder extends RecyclerView.ViewHolder {
    ImageView mImage;
    public ImageViewHolder(View itemView) {
        super (itemView);
        // init views...
    }
}

private static final int TYPE_IMAGE = 1;
private static final int TYPE_GROUP = 2;  

E poi, in fase di esecuzione, fare qualcosa del genere:

@Override
public int getItemViewType(int position) {
    // here your custom logic to choose the view type
    return position == 0 ? TYPE_IMAGE : TYPE_GROUP;
}

@Override
public void onBindViewHolder (ViewHolder viewHolder, int i) {

    switch (viewHolder.getItemViewType()) {

        case TYPE_IMAGE:
            ImageViewHolder imageViewHolder = (ImageViewHolder) viewHolder;
            imageViewHolder.mImage.setImageResource(...);
            break;

        case TYPE_GROUP:
            GroupViewHolder groupViewHolder = (GroupViewHolder) viewHolder;
            groupViewHolder.mContent.setText(...)
            groupViewHolder.mTitle.setText(...);
            break;
    }
}

Spero che sia d'aiuto.


3
Questa è la risposta diretta alla domanda. L'unica parte mancante è la necessità di sovrascrivere onCreateViewHolder (ViewGroup parent, int viewType) e gestire diversi tipi di visualizzazione in base a viewType
user1928896,

Un altro esempio per infalting le diverse righe all'interno del recyelview cioè: - stackoverflow.com/questions/39971350/...
Ravindra Kushwaha

1
Esiste una soluzione generica invece di fondere la base del supporto vista sui valori della custodia dell'interruttore?
Vahid Ghadiri,

33

Secondo la grande risposta di Gil, ho risolto sostituendo getItemViewType come spiegato da Gil. La sua risposta è ottima e deve essere contrassegnata come corretta. In ogni caso, aggiungo il codice per raggiungere il punteggio:

Nel tuo adattatore per riciclatore:

@Override
public int getItemViewType(int position) {
    int viewType = 0;
    // add here your booleans or switch() to set viewType at your needed
    // I.E if (position == 0) viewType = 1; etc. etc.
    return viewType;
}

@Override
public FileViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == 0) {
        return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout_for_first_row, parent, false));
    }

    return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_other_rows, parent, false));
}

In questo modo, puoi impostare qualsiasi layout personalizzato per qualsiasi riga!


18
Solo un piccolo commento: il secondo parametro in onCreateViewHolder dovrebbe essere viewType, non l'indice. Secondo API: developer.android.com/reference/android/support/v7/widget/… , int)
Mark Martinsson

ma che dire quando l'utente scorre velocemente in quel momento qualche strano output che sto ricevendo.
Rjz Satvara,

15

È abbastanza complicato ma molto difficile, basta copiare il codice qui sotto e il gioco è fatto

package com.yuvi.sample.main;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;


import com.yuvi.sample.R;

import java.util.List;

/**
 * Created by yubraj on 6/17/15.
 */

public class NavDrawerAdapter extends RecyclerView.Adapter<NavDrawerAdapter.MainViewHolder> {
    List<MainOption> mainOptionlist;
    Context context;
    private static final int TYPE_PROFILE = 1;
    private static final int TYPE_OPTION_MENU = 2;
    private int selectedPos = 0;
    public NavDrawerAdapter(Context context){
        this.mainOptionlist = MainOption.getDrawableDataList();
        this.context = context;
    }

    @Override
    public int getItemViewType(int position) {
        return (position == 0? TYPE_PROFILE : TYPE_OPTION_MENU);
    }

    @Override
    public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType){
            case TYPE_PROFILE:
                return new ProfileViewHolder(LayoutInflater.from(context).inflate(R.layout.row_profile, parent, false));
            case TYPE_OPTION_MENU:
                return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.row_nav_drawer, parent, false));
        }
        return null;
    }

    @Override
    public void onBindViewHolder(MainViewHolder holder, int position) {
        if(holder.getItemViewType() == TYPE_PROFILE){
            ProfileViewHolder mholder = (ProfileViewHolder) holder;
            setUpProfileView(mholder);
        }
        else {
            MyViewHolder mHolder = (MyViewHolder) holder;
            MainOption mo = mainOptionlist.get(position);
            mHolder.tv_title.setText(mo.title);
            mHolder.iv_icon.setImageResource(mo.icon);
            mHolder.itemView.setSelected(selectedPos == position);
        }
    }

    private void setUpProfileView(ProfileViewHolder mholder) {

    }

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




public class MyViewHolder extends MainViewHolder{
    TextView tv_title;
    ImageView iv_icon;

    public MyViewHolder(View v){
        super(v);
        this.tv_title = (TextView) v.findViewById(R.id.tv_title);
        this.iv_icon = (ImageView) v.findViewById(R.id.iv_icon);
        v.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Redraw the old selection and the new
                notifyItemChanged(selectedPos);
                selectedPos = getLayoutPosition();
                notifyItemChanged(selectedPos);
            }
        });
    }
}
    public class ProfileViewHolder extends MainViewHolder{
        TextView tv_name, login;
        ImageView iv_profile;

        public ProfileViewHolder(View v){
            super(v);
            this.tv_name = (TextView) v.findViewById(R.id.tv_profile);
            this.iv_profile = (ImageView) v.findViewById(R.id.iv_profile);
            this.login = (TextView) v.findViewById(R.id.tv_login);
        }
    }

    public void trace(String tag, String message){
        Log.d(tag , message);
    }
    public class MainViewHolder extends  RecyclerView.ViewHolder {
        public MainViewHolder(View v) {
            super(v);
        }
    }


}

godere !!!!


My Viewholder1 ha un layout chiamato myLaout1.xml e contiene ScrollView. Quindi ora quando scorro questa cosa viene fatta scorrere il recyclerview. Come scorrere il contenuto di
Viewholder1

3

Possiamo ottenere una visualizzazione multipla su un singolo RecyclerView dal basso: -

Dipendenze da Gradle quindi aggiungi sotto il codice: -

compile 'com.android.support:cardview-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.1'

RecyclerView in XML

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Codice attività

private RecyclerView mRecyclerView;
private CustomAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private String[] mDataset = {“Data - one ”, Data - two”,
    Showing data three”, Showing data four”};
private int mDatasetTypes[] = {DataOne, DataTwo, DataThree}; //view types
 
...
 
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setLayoutManager(mLayoutManager);
//Adapter is created in the last step
mAdapter = new CustomAdapter(mDataset, mDataSetTypes);
mRecyclerView.setAdapter(mAdapter);

Primo XML

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/ten"
    android:elevation="@dimen/hundered”
    card_view:cardBackgroundColor=“@color/black“>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding=“@dimen/ten">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=“Fisrt”
            android:textColor=“@color/white“ />
 
        <TextView
            android:id="@+id/temp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:textColor="@color/white"
            android:textSize="30sp" />
    </LinearLayout>
 
</android.support.v7.widget.CardView>

Secondo XML

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/ten"
    android:elevation="100dp"
    card_view:cardBackgroundColor="#00bcd4">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="@dimen/ten">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=“DataTwo”
            android:textColor="@color/white" />
 
        <TextView
            android:id="@+id/score"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:textColor="#ffffff"
            android:textSize="30sp" />
    </LinearLayout>
 
</android.support.v7.widget.CardView>

Terzo XML

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/ten"
    android:elevation="100dp"
    card_view:cardBackgroundColor="@color/white">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="@dimen/ten">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=“DataThree” />
 
        <TextView
            android:id="@+id/headline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:textSize="25sp" />
 
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:id="@+id/read_more"
            android:background="@color/white"
            android:text=“Show More/>
    </LinearLayout>
 
</android.support.v7.widget.CardView>

Ora è il momento di creare un adattatore e questo è principale per mostrare diverse -2 viste sulla stessa vista del riciclatore, quindi ti preghiamo di controllare completamente questo focus del codice: -

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private static final String TAG = "CustomAdapter";
 
    private String[] mDataSet;
    private int[] mDataSetTypes;
 
    public static final int dataOne = 0;
    public static final int dataTwo = 1;
    public static final int dataThree = 2;
 
 
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }
 
    public class DataOne extends ViewHolder {
        TextView temp;
 
        public DataOne(View v) {
            super(v);
            this.temp = (TextView) v.findViewById(R.id.temp);
        }
    }
 
    public class DataTwo extends ViewHolder {
        TextView score;
 
        public DataTwo(View v) {
            super(v);
            this.score = (TextView) v.findViewById(R.id.score);
        }
    }
 
    public class DataThree extends ViewHolder {
        TextView headline;
        Button read_more;
 
        public DataThree(View v) {
            super(v);
            this.headline = (TextView) v.findViewById(R.id.headline);
            this.read_more = (Button) v.findViewById(R.id.read_more);
        }
    }
 
 
    public CustomAdapter(String[] dataSet, int[] dataSetTypes) {
        mDataSet = dataSet;
        mDataSetTypes = dataSetTypes;
    }
 
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View v;
        if (viewType == dataOne) {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.weather_card, viewGroup, false);
 
            return new DataOne(v);
        } else if (viewType == dataTwo) {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.news_card, viewGroup, false);
            return new DataThree(v);
        } else {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.score_card, viewGroup, false);
            return new DataTwo(v);
        }
    }
 
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        if (viewHolder.getItemViewType() == dataOne) {
            DataOne holder = (DataOne) viewHolder;
            holder.temp.setText(mDataSet[position]);
        }
        else if (viewHolder.getItemViewType() == dataTwo) {
            DataThree holder = (DataTwo) viewHolder;
            holder.headline.setText(mDataSet[position]);
        }
        else {
            DataTwo holder = (DataTwo) viewHolder;
            holder.score.setText(mDataSet[position]);
        }
    }
 
    @Override
    public int getItemCount() {
        return mDataSet.length;
    }
 
   @Override
    public int getItemViewType(int position) {
        return mDataSetTypes[position];
    }
}

Puoi controllare anche questo link per maggiori informazioni.


ma funziona benissimo ma quando sto scorrendo velocemente dall'alto verso il basso e viceversa sto ottenendo un risultato strano ... significa che i dati non sono impostati correttamente. qual è la sua soluzione?
Rjz Satvara,

2

Devi implementare il getItemViewType()metodo in RecyclerView.Adapter. Per impostazione predefinita, onCreateViewHolder(ViewGroup parent, int viewType)l'attuazione viewTypedi questo metodo restituisce 0. Innanzitutto è necessario visualizzare il tipo di articolo in posizione ai fini del riciclaggio della vista e per questo è necessario sostituire il getItemViewType()metodo in cui è possibile passare viewTypeche restituirà la posizione dell'articolo. Di seguito è riportato un esempio di codice

@Override
public MyViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
    int listViewItemType = getItemViewType(viewType);
    switch (listViewItemType) {
         case 0: return new ViewHolder0(...);
         case 2: return new ViewHolder2(...);
    }
}

@Override
public int getItemViewType(int position) {   
    return position;
}

// and in the similar way you can set data according 
// to view holder position by passing position in getItemViewType
@Override
public void onBindViewHolder(MyViewholder viewholder, int position) {
    int listViewItemType = getItemViewType(position);
    // ...
}

2

getItemViewType (int position) è la chiave

Secondo me, il punto di partenza per creare questo tipo di recyclerView è la conoscenza di questo metodo. Poiché questo metodo è facoltativo da sovrascrivere, pertanto non è visibile nella classe RecylerView per impostazione predefinita, il che a sua volta fa sì che molti sviluppatori (incluso me) si chiedano da dove cominciare. Una volta che sai che esiste questo metodo, la creazione di tale RecyclerView sarebbe un gioco da ragazzi.

inserisci qui la descrizione dell'immagine

Come farlo ?

È possibile creare un RecyclerViewcon qualsiasi numero di viste diverse (ViewHolders). Ma per una migliore leggibilità facciamo un esempio di RecyclerViewcon due Viewholders.
Ricorda questi 3 semplici passaggi e sarai a posto.

  • Sostituisci int pubblico getItemViewType(int position)
  • Restituisce ViewHolder diversi in base al ViewTypemetodo in onCreateViewHolder ()
  • Popola vista in base all'elemento itemType nel onBindViewHolder()metodo

    Ecco uno snippet di codice per te

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

Codice GitHub:

Ecco un progetto in cui ho implementato un RecyclerView con più ViewHolder.


Che dire della stessa cosa ma che hanno anche più set di dati?
esQmo_

Cosa intendi? @esQmo_
Rohit Singh,

Voglio dire cosa succede se ogni vieholder ha anche un set di dati diverso (origine dati)?
esQmo_

1

Puoi semplicemente restituire ItemViewType e usarlo. Vedi sotto il codice:

@Override
public int getItemViewType(int position) {

    Message item = messageList.get(position);
    // return my message layout
    if(item.getUsername() == Message.userEnum.I)
        return R.layout.item_message_me;
    else
        return R.layout.item_message; // return other message layout
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(viewType, viewGroup, false);
    return new ViewHolder(view);
}

1

È possibile utilizzare la libreria: 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 */

Per ogni elemento, è necessario implementare un ViewRenderer, ViewHolder, SomeModel:

ViewHolder - è un semplice supporto per la visualizzazione della vista del riciclatore.

SomeModel - è il tuo modello con ItemModelinterfaccia

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

Per maggiori dettagli puoi consultare la documentazione.


0

Puoi utilizzare questa libreria:
https://github.com/kmfish/MultiTypeListViewAdapter (scritto da me)

  • Meglio riutilizzare il codice di una cella
  • Migliore espansione
  • Migliore disaccoppiamento

Adattatore di installazione:

adapter = new BaseRecyclerAdapter();
adapter.registerDataAndItem(TextModel.class, LineListItem1.class);
adapter.registerDataAndItem(ImageModel.class, LineListItem2.class);
adapter.registerDataAndItem(AbsModel.class, AbsLineItem.class);

Per ogni elemento pubblicitario:

public class LineListItem1 extends BaseListItem<TextModel, LineListItem1.OnItem1ClickListener> {

    TextView tvName;
    TextView tvDesc;


    @Override
    public int onGetLayoutRes() {
        return R.layout.list_item1;
    }

    @Override
    public void bindViews(View convertView) {
        Log.d("item1", "bindViews:" + convertView);
        tvName = (TextView) convertView.findViewById(R.id.text_name);
        tvDesc = (TextView) convertView.findViewById(R.id.text_desc);

        tvName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != attachInfo) {
                    attachInfo.onNameClick(getData());
                }
            }
        });
        tvDesc.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != attachInfo) {
                    attachInfo.onDescClick(getData());
                }
            }
        });

    }

    @Override
    public void updateView(TextModel model, int pos) {
        if (null != model) {
            Log.d("item1", "updateView model:" + model + "pos:" + pos);
            tvName.setText(model.getName());
            tvDesc.setText(model.getDesc());
        }
    }

    public interface OnItem1ClickListener {
        void onNameClick(TextModel model);
        void onDescClick(TextModel model);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.