Ce sera une question théorique.
Comme tout le monde, nous utilisons RecyclerView dans de nombreuses parties de l'application. Parfois, RecyclerView contient différents éléments, pas seulement des images, par exemple, mais des annonces, des astuces, etc. C'est pourquoi nous pouvons utiliser la méthode getViewType () dans Adapter.
Mais le problème survient lorsque nous avons plusieurs viewTypes et que la liaison dans Adapter n’est pas élégante. Donc, voici la question, est-ce un bon modèle pour lier les données dans ViewHolder?
Disons que nous avons une liste d'applications.
Chaque application a un nom pour la simplicité. Notre ViewHolder ressemble à ceci:
class AppViewHolder extends RecyclerView.ViewHolder {
public TextView nameText;
AppViewHolder(View itemView) {
super(itemView)
nameText = (TextView) itemView.findViewById(R.id.text_name);
}
}
Maintenant nous pourrions ajouter la méthode bind:
public void bind(App app) {
nameText.setText(app.getName());
}
Est-ce un bon motif?
Une autre solution serait d'utiliser ViewModel. Comme nous avons différents éléments dans RecyclerView, notre adaptateur peut contenir une liste de classe qui est la classe de base pour chaque ViewModel.
Donc, le cours de base est:
class RecyclerViewItem {}
Maintenant, classe qui est ViewModel for App.
class AppRecyclerViewItem extends RecyclerViewItem {
App app;
...
}
et notre adaptateur a juste la liste de RecyclerViewItems:
class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<RecyclerViewItem> items;
...
}
Donc, avec cette approche (je veux dire en utilisant ViewModel) est-il préférable d’ajouter une méthode de liaison dans ViewHolder, ou dans ViewModel?
Je dirais qu'aucune de vos solutions n'est bonne. La première est valable uniquement pour les éléments n'apparaissant qu'une fois dans l'adaptateur. Créer une méthode pour remplir une ligne dans ViewHolder
n'est pas une bonne solution car différents types de ViewHolders
augmenteraient et votre RecyclerView.Adapter
gagnerait de plus en plus de lignes.
La deuxième méthode n'est pas bonne non plus. Le modèle est le modèle, ne doit contenir que des données et non la logique applicative de l'entreprise.
Je suggérerais une solution pour garder RecyclerView.Adapter
propre et passer la logique de créer ViewHolder
et de les renseigner avec des données pour les délégués, ce qui serait enregistré dans ie. RecyclerView.Adapter
constructeur. Cette technique est expliquée plus ici
De cette façon, s’enregistrer même sur un nombre différent de ViewHolder
n’ajoute pas spécialement un passe-partout à RecyclerView.Adapter
, mais délègue la logique de création et de remplissage des rangées RecyclerView.Adapter
à ces délégués.
Mais ce n'est qu'une suggestion.
Il existe une excellente solution en utilisant des délégués - http://hannesdorfmann.com/Android/adapter-delegates et il peut même être utilisé pour DataBinding (avec de simples modifications, https://github.com/drstranges/DataBinding_For_RecyclerView ). De cette manière, les données liées non pas dans ViewHolder, ni dans RecyclerView Adapter mais ItemDelegate, de sorte que votre code reste clair et lisible.
En suivant cette approche, tous fonctionnent avec des types de vues délégués au DelegateManager:
public abstract class AbsDelegationAdapter<T> extends RecyclerView.Adapter {
protected AdapterDelegatesManager<T> delegatesManager;
protected T items;
//...
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return delegatesManager.onCreateViewHolder(parent, viewType);
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
delegatesManager.onBindViewHolder(items, position, holder);
}
@Override public int getItemViewType(int position) {
return delegatesManager.getItemViewType(items, position);
}
}
Et votre code ressemblera à ceci:
mAdapter = new BindableAdapter<>(
new UserDelegate(actionHandler), // you can create custom delegate
//new ModelActionItemDelegate<BaseModel>(actionHandler, User.class, R.layout.item_user, BR.user), // or use generic
new AnotherItemDelegate());
Où ItemDelegate
peut ressembler à ceci:
public class UserDelegate extends BaseAdapterDelegate<BaseModel, ItemUserBinding> {
@Override
public boolean isForViewType(@NonNull final List<BaseModel> items, final int position) {
return items.get(position) instanceof User;
}
@NonNull
@Override
public BindingHolder<ItemUserBinding> onCreateViewHolder(final ViewGroup parent) {
// create ViewHolder
return BindingHolder.newInstance(R.layout.item_user, LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
public void onBindViewHolder(@NonNull final List<BaseModel> items, final int position, @NonNull final BindingHolder<ItemUserBinding> holder) {
// Just bind data
final User user = (User) items.get(position);
holder.getBinding().setUser(user);
}
}
Pour mes applications, j'utilise quelque chose comme ceci:
public abstract class BindableViewHolder<T> extends RecyclerView.ViewHolder {
public BindableViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(T thing);
}
L'idée est de déléguer l'implémentation de la liaison au ViewHolder . Cette solution est destinée aux cas où vous souhaitez lier le même type d’objet à différents viewholders, mais je pense qu’il est facilement extensible à des cas plus généraux.
Voici un exemple d'adaptateur très simple. Je pense que cela s’explique, espérons que cela aidera!
public class ShowsAdapter extends RecyclerView.Adapter<BindableViewHolder<Show>> {
private final DataProvider<Show> showsProvider;
public ShowsAdapter(DataProvider<Show> showsProvider) {
this.showsProvider = showsProvider;
}
@Override
public BindableViewHolder<Show> onCreateViewHolder(ViewGroup parent, int viewType) {
return ShowViewHolder.create(parent);
}
@Override
public void onBindViewHolder(BindableViewHolder<Show> holder, int position) {
holder.bind(showsProvider.get(position));
}
@Override
public int getItemCount() {
return showsProvider.size();
}
}
ShowViewHolder est une sous-classe concrète de BindableViewHolder,