Vue d'ensemble du recyclage et gestion de différents types de gonflage de rangs

124

J'essaie de travailler avec le nouveau RecyclerView, mais je n'ai pas pu trouver d'exemple RecyclerViewavec différents types de lignes / vues de cartes gonflées.

Avec ListViewje remplace le getViewTypeCountet getItemViewType, pour gérer différents types de lignes.

Suis-je censé le faire comme "à l'ancienne" ou devrais-je faire quelque chose avec LayoutManager? Je me demandais si quelqu'un pouvait m'indiquer la bonne direction. Parce que je ne peux trouver que des exemples avec un seul type.

Je veux avoir une liste de cartes légèrement différentes. Ou devrais-je simplement utiliser un scrollViewavec à l' cardViewsintérieur de celui-ci ... le faire sans l'adaptateur et recyclerView?

Lokkio
la source
quelle est la différence entre vos types d'articles? comment le recyclerview devrait-il réagir aux différents types? En général, vous ne pouvez rien faire avec un scrollview / listview que vous ne pouvez pas faire avec un recyclerview, mais pas l'inverse
Gil Moshayof
C'est en fait comme ce que vous voyez dans le Google Play Store. En haut, vous pouvez avoir un en-tête, puis vous pouvez voir trois cartes, puis vous avez une section avec des informations. Est-ce fait dans un recyclerview / listview? Ou scrollview? Parce que si c'est un scrollview, je dois déterminer toutes les mises en page avant. Avec une liste, je peux simplement ajouter certains objets à mon ensemble de données et la bonne mise en page sera gonflée. Donc je veux savoir, comment faire la dernière partie avec le nouveau Recyclerview, dois-je remplacer des méthodes comme listview?
Lokkio
AnyOne à la recherche d'une démo github pour la mise en page sur plusieurs lignes à l'aide du recycleur code2concept.blogspot.in/2015/10
...
Vérifiez ces liens, cela fonctionnera pour vous: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

Réponses:

202

La gestion de la logique des lignes / sections similaire à UITableView d'iOS n'est pas aussi simple dans Android que dans iOS, cependant, lorsque vous utilisez RecyclerView, la flexibilité de ce que vous pouvez faire est bien plus grande.

En fin de compte, tout dépend de la façon dont vous déterminez le type de vue que vous affichez dans l'adaptateur. Une fois que vous avez compris cela, la navigation devrait être facile (pas vraiment, mais au moins vous l'aurez triée).

L'adaptateur expose deux méthodes que vous devez remplacer:

getItemViewType(int position)

L'implémentation par défaut de cette méthode retournera toujours 0, indiquant qu'il n'y a qu'un seul type de vue. Dans votre cas, ce n'est pas le cas, et vous devrez donc trouver un moyen d'affirmer quelle ligne correspond à quel type de vue. Contrairement à iOS, qui gère cela pour vous avec des lignes et des sections, vous n'aurez ici qu'un seul index sur lequel vous appuyer, et vous devrez utiliser vos compétences de développeur pour savoir quand une position est en corrélation avec un en-tête de section, et quand elle correspond à une ligne normale.

createViewHolder(ViewGroup parent, int viewType)

Vous devez quand même remplacer cette méthode, mais généralement, les gens ignorent simplement le paramètre viewType. En fonction du type de vue, vous devrez gonfler la bonne ressource de mise en page et créer votre support de vue en conséquence. Le RecyclerView gérera le recyclage de différents types de vues de manière à éviter les conflits entre différents types de vues.

Si vous prévoyez d'utiliser un LayoutManager par défaut, tel que LinearLayoutManager, vous devriez être prêt à partir. Si vous envisagez de créer votre propre implémentation de LayoutManager, vous devrez travailler un peu plus dur. La seule API avec laquelle vous devez vraiment travailler est celle findViewByPosition(int position)qui donne une vue donnée à une certaine position. Étant donné que vous voudrez probablement le présenter différemment en fonction du type de cette vue, vous avez quelques options:

  1. Généralement, lorsque vous utilisez le modèle ViewHolder, vous définissez la balise de la vue avec le support de vue. Vous pouvez l'utiliser pendant l'exécution dans le gestionnaire de mise en page pour savoir de quel type est la vue en ajoutant un champ dans le support de vue qui l'exprime.

  2. Puisque vous aurez besoin d'une fonction qui détermine quelle position correspond à quel type de vue, vous pouvez aussi bien rendre cette méthode globalement accessible d'une manière ou d'une autre (peut-être une classe singleton qui gère les données?), Et ensuite vous pouvez simplement interroger la même méthode en fonction de la position.

Voici un exemple de code:

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

Voici un exemple de l'apparence de ces détenteurs de vue:

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

Bien sûr, cet exemple suppose que vous avez construit votre source de données (myData) d'une manière qui facilite l'implémentation d'un adaptateur de cette manière. À titre d'exemple, je vais vous montrer comment je construirais une source de données qui montre une liste de noms et un en-tête pour chaque fois que la 1ère lettre du nom change (en supposant que la liste est alphabétisée) - similaire à la façon dont un contacts la liste ressemblerait à:

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

Cet exemple vient résoudre un problème assez spécifique, mais j'espère que cela vous donne un bon aperçu de la façon de gérer différents types de lignes dans un recycleur et vous permet de faire les adaptations nécessaires dans votre propre code pour répondre à vos besoins.

Gil Moshayof
la source
Assez génial, et je pense que c'est un excellent exemple pour résoudre ce problème Exemple de solution
Amt87
Un autre exemple pour infaler les différentes lignes à l'intérieur de la recyelview qui est: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha
Devrait êtrenames[i].substring(0, 1)
Kyle
1
En outre, pour les vues de recycleur avec des éléments hétérogènes, il est également judicieux de consulter SpanSizeLookup. stackoverflow.com/questions/26869312/…
Mahori
C'est utile. Sur la base de cette réponse, j'ai également une idée d'implémenter la vue multi-type dans Adapter en utilisant enum. L'énumération aura une méthode onCreateViewHolderqui nous aidera à créer un support de vue. Pour plus de détails, vous pouvez consulter ma part: stackoverflow.com/questions/47245398/…
quangson91
114

L'astuce consiste à créer des sous-classes de ViewHolder, puis à les convertir.

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;  

Et puis, au moment de l'exécution, faites quelque chose comme ceci:

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

J'espère que ça aide.

ticofab
la source
3
C'est la réponse directe à la question. La seule partie manquante est la nécessité de remplacer onCreateViewHolder (ViewGroup parent, int viewType) et de gérer différents types de vues en fonction de viewType
user1928896
Un autre exemple pour infaler les différentes lignes à l'intérieur du recyelview qui est: - stackoverflow.com/questions/39971350/…
Ravindra Kushwaha
1
Existe-t-il une solution générique au lieu de diffuser la base du support de vue sur les valeurs de cas de commutation?
Vahid Ghadiri
33

Selon Gil, une excellente réponse que j'ai résolue en remplaçant le getItemViewType comme expliqué par Gil. Sa réponse est excellente et doit être marquée comme correcte. Dans tous les cas, j'ajoute le code pour atteindre le score:

Dans votre adaptateur recycleur:

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

En faisant cela, vous pouvez définir n'importe quelle disposition personnalisée pour n'importe quelle ligne!

iGio90
la source
18
Juste un commentaire mineur: le deuxième paramètre dans onCreateViewHolder doit être le viewType, pas l'index. Selon l'API: developer.android.com/reference/android/support/v7/widget/… , int)
Mark Martinsson
mais qu'en est-il lorsque l'utilisateur fait défiler rapidement à ce moment-là une sortie étrange que je reçois.
Rjz Satvara
15

C'est assez délicat mais trop difficile, copiez simplement le code ci-dessous et vous avez terminé

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


}

prendre plaisir !!!!

yubaraj poudel
la source
Mon Viewholder1 a une disposition nommée myLaout1.xml et contient ScrollView. Alors maintenant, quand je fais défiler cette chose, la vue de recyclage est défilée. Comment faire défiler le contenu de
Viewholder1
3

Nous pouvons obtenir plusieurs vues sur un seul RecyclerView de la manière ci-dessous: -

Dépendances sur Gradle alors ajoutez le code ci-dessous: -

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

RecyclerView en XML

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

Code d'activité

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

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

Deuxième 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>

Troisième 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>

Il est maintenant temps de créer un adaptateur et c'est principal pour afficher une vue -2 différente sur la même vue du recycleur.Veuillez donc vérifier complètement ce code: -

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

Vous pouvez également consulter ce lien pour plus d'informations.

duggu
la source
mais cela fonctionne bien, mais quand je défile rapidement de haut en bas et vice versa, j'obtiens une sortie étrange ... signifie que les données ne sont pas définies correctement. quelle est sa solution?
Rjz Satvara
2

Vous devez implémenter la getItemViewType()méthode dans RecyclerView.Adapter. Par défaut, l' onCreateViewHolder(ViewGroup parent, int viewType)implémentation viewTypede cette méthode retourne 0. Tout d'abord, vous avez besoin du type de vue de l'élément à la position à des fins de recyclage de la vue et pour cela, vous devez remplacer la getItemViewType()méthode dans laquelle vous pouvez transmettre viewTypequi renverra votre position d'élément. Un exemple de code est donné ci-dessous

@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);
    // ...
}
Amandeep Rohila
la source
2

getItemViewType (position int) est la clé

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

entrez la description de l'image ici

Comment faire ?

Vous pouvez créer un RecyclerViewavec n'importe quel nombre de vues différentes (ViewHolders). Mais pour une meilleure lisibilité, prenons un exemple de RecyclerViewavec deux Viewholders.
N'oubliez pas ces 3 étapes simples et vous serez prêt à partir.

  • Remplacer public int getItemViewType(int position)
  • Renvoie différents ViewHolders en fonction de la ViewTypeméthode in onCreateViewHolder ()
  • Remplir la vue en fonction de la onBindViewHolder()méthode itemViewType in

    Voici un extrait de code pour vous

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

Code GitHub:

Voici un projet où j'ai implémenté un RecyclerView avec plusieurs ViewHolders.

Rohit Singh
la source
Qu'en est-il de la même chose, mais ayant également plusieurs ensembles de données?
esQmo_
Que voulez-vous dire? @esQmo_
Rohit Singh
Je veux dire que se passe-t-il si chaque vieholder a également un ensemble de données différent (source de données)?
esQmo_
1

Vous pouvez simplement renvoyer ItemViewType et l'utiliser. Voir le code ci-dessous:

@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);
}
김영호
la source
1

Vous pouvez utiliser la bibliothèque: https://github.com/vivchar/RendererRecyclerViewAdapter

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

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

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

SomeModel - c'est votre modèle avec ItemModelinterface

public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> {

    public SomeViewRenderer(final int type, final Context context) {
        super(type, context);
    }

    @Override
    public void bindView(@NonNull final SomeModel model, @NonNull final SomeViewHolder holder) {
       holder.mTitle.setText(model.getTitle());
    }

    @NonNull
    @Override
    public SomeViewHolder createViewHolder(@Nullable final ViewGroup parent) {
        return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false));
    }
}

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

Vitaly
la source
0

Vous pouvez utiliser cette bibliothèque:
https://github.com/kmfish/MultiTypeListViewAdapter (écrit par moi)

  • Mieux vaut réutiliser le code d'une cellule
  • Meilleure expansion
  • Meilleur découplage

Adaptateur de configuration:

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

Pour chaque élément de campagne:

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);
    }
}
kmfish
la source