2016-10-09 1 views
9

Ce sera une question théorique.Reliure dans ViewHolder

Comme tout le monde, nous utilisons RecyclerView dans de nombreuses parties de l'application. Parfois, RecyclerView contient des éléments différents, pas seulement de l'image par exemple, mais des publicités, des conseils, etc. Et c'est pourquoi nous pouvons utiliser la méthode getViewType() dans Adapter.

Mais le problème se produit lorsque nous avons plusieurs viewTypes et que la liaison dans Adapter n'est pas élégante. Donc, voici la question, est-il agréable et bon modèle pour lier des données dans ViewHolder?

Disons que nous avons une liste d'applications.

Chaque application a un nom pour plus de 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, on pourrait ajouter méthode bind:

public void bind(App app) { 
    nameText.setText(app.getName()); 
} 

Est-il bon modèle?

Une autre solution serait d'utiliser ViewModel. Parce que 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.

classe de base est donc:

class RecyclerViewItem {} 

Maintenant classe qui est ViewModel pour App.

class AppRecyclerViewItem extends RecyclerViewItem { 

    App app; 

    ... 
} 

et notre adaptateur a juste la liste des 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 la méthode de liaison à ViewHolder, ou ViewModel?

Répondre

6

Je dirais qu'aucune de vos solutions n'est bonne. Le premier n'est bon que pour les articles qui n'apparaissent qu'une seule fois dans l'adaptateur. Créer une méthode pour remplir la ligne à l'intérieur ViewHolder n'est pas une bonne solution car le type différent de ViewHolders augmenterait et votre RecyclerView.Adapter gagnerait de plus en plus de lignes.

La deuxième méthode n'est également pas bonne. Le modèle est le modèle, ne devrait contenir que des données et non la logique métier de l'application.

Je suggère une solution pour garder RecyclerView.Adapter propre et passer la logique de la création ViewHolder et les remplir avec des données aux délégués, qui serait enregistré dans IE. RecyclerView.Adapter constructeur. Cette technique est expliquée plus here

De cette façon, l'enregistrement sur une même doven de différents ViewHolder ne pas ajouter à expecially boilerplate RecyclerView.Adapter, mais délègue la logique de la création et le remplissage RecyclerView.Adapter lignes à ces délégués.

Mais ce n'est qu'une suggestion.

+0

Je pense que la première méthode est bonne surtout parce que dans recyclerview onBindViewHolder w Appelez bind dans le viewholder, donc l'adaptateur ne gagnera pas beaucoup de lignes. Mais j'aime l'idée que tu as suggérée, je vais y plonger. – ThirdMartian

+2

Utilisez-vous cette approche dans votre travail? Pouvez-vous partager votre expérience? – ThirdMartian

0

Pour mes applications je me sers 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égué la mise en œuvre de la liaison au ViewHolder. Cette solution est destinée aux cas où vous voulez 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 ça s'explique, j'espère que ça aide!

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

Ici, ShowViewHolder est une sous-classe concrète de BindableViewHolder,

0

Il y a une grande solution à l'aide des délégués - http://hannesdorfmann.com/android/adapter-delegates et il peut même être utilisé pour DataBinding (avec des changements simples, https://github.com/drstranges/DataBinding_For_RecyclerView). De cette façon, les données ne sont pas liées dans ViewHolder et pas dans RecyclerView Adapter, mais ItemDelegate le fait, de sorte que votre code reste clair et lisible.

Suite à cette approche tous les travaux avec vue types délégué 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()); 

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

}