web-dev-qa-db-fra.com

Pourquoi l'ajout d'un OnClickListener à l'intérieur de onBindViewHolder d'un RecyclerView.Adapter est-il considéré comme une mauvaise pratique?

J'ai le code suivant pour une classe RecyclerView.Adapter et cela fonctionne bien:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.Viewholder> {

    private List<Information> items;
    private int itemLayout;

    public MyAdapter(List<Information> items, int itemLayout){
        this.items = items;
        this.itemLayout = itemLayout;
    }

    @Override
    public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
        return new Viewholder(v);
    }

    @Override
    public void onBindViewHolder(Viewholder holder, final int position) {
        Information item = items.get(position);
        holder.textView1.setText(item.Title);
        holder.textView2.setText(item.Date);

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(view.getContext(), "Recycle Click" + position, Toast.LENGTH_SHORT).show();
            }
        });

       holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
       @Override
       public boolean onLongClick(View v) {
          Toast.makeText(v.getContext(), "Recycle Click" + position, Toast.LENGTH_SHORT).show();
           return true;
       }
});
    }

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

    public class Viewholder extends RecyclerView.ViewHolder {
        public  TextView textView1;
        public TextView textView2;

        public Viewholder(View itemView) {
            super(itemView);
            textView1=(TextView) itemView.findViewById(R.id.text1);
            textView2 = (TextView) itemView.findViewById(R.id.date_row);

        }
    }
}

Cependant, j'estime qu'il est déconseillé d'implémenter OnClickListener dans la méthode onBindViewHolder. Pourquoi est-ce une mauvaise pratique et quelle est la meilleure alternative?

54
Sujit Yadav

Il est préférable de gérer votre logique de clic dans ViewHolder, car elle permet d’écouter des écouteurs de clic plus explicites. Comme indiqué dans le livre Commonsware:

Les widgets cliquables, comme un RatingBar, dans une ligne ListView étaient depuis longtemps en conflit avec les événements de clic sur les lignes elles-mêmes. Obtenir des lignes sur lesquelles il est possible de cliquer, avec le contenu de ligne sur lequel on peut également cliquer, devient parfois un peu délicat. Avec RecyclerView, vous avez un contrôle plus explicite sur la façon dont ce genre de choses est traité… car vous êtes le seul à configurer toute la logique de traitement par clic.

En utilisant le modèle ViewHolder, vous pouvez bénéficier de nombreux avantages en termes de gestion des clics dans RecyclerView par rapport à ListView. J'ai écrit à ce sujet dans un article de blog comparant les différences - https://androidessence.com/Android/recyclerview-vs-listview/

En ce qui concerne la raison pour laquelle il vaut mieux utiliser ViewHolder que onBindViewHolder(), c'est parce que onBindViewHolder() est appelé pour chaque élément et que définir l'écouteur de clic est une option inutile à répéter lorsque vous pouvez l'appeler une fois dans votre constructeur ViewHolder. Ensuite, si votre clic répond en fonction de la position de l'élément sur lequel vous avez cliqué, vous pouvez simplement appeler getAdapterPosition() à partir de ViewHolder. Ici est une autre réponse que j'ai donnée qui montre comment utiliser la variable OnClickListener à partir de votre classe ViewHolder.

50
AdamMc331

La méthode onCreateViewHolder() sera appelée plusieurs fois une ViewHolder est nécessaire de chaque viewType. La méthode onBindViewHolder() sera appelée chaque fois qu'un nouvel élément apparaît dans la vue ou que ses données changent. Vous voulez éviter toute opération coûteuse dans onBindViewHolder() car cela pourrait ralentir votre défilement. Ceci est moins préoccupant dans onCreateViewHolder(). Ainsi, il est généralement préférable de créer des éléments tels que OnClickListeners dans onCreateViewHolder() afin qu’ils ne se produisent qu’une fois par objet ViewHolder. Vous pouvez appeler getLayoutPosition() dans l'écouteur pour obtenir la position actuelle, plutôt que de prendre l'argument position fourni à onBindViewHolder()

13
Brucelet

La méthode onBindViewHolder est appelée chaque fois que vous liez votre vue à un objet qui n'a tout simplement pas été vu. Et chaque fois que vous ajouterez un nouvel auditeur. Tu devrais faire ça 

@Override
public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
     View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
     final ViewHolder holder = new ViewHolder(v);

     holder.itemView.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             Log.d(TAG, "position = " + holder.getAdapterPosition());
         }
     });
     return holder;
}
7
Pavel Kozemirov

Pavel fourni excellent exemple de code, sauf une ligne à la fin. Vous devez retourner le titulaire créé. Pas le nouveau Viewholder (v).

@Override
public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
     View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
     final ViewHolder holder = new ViewHolder(v);

     holder.itemView.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             Log.d(TAG, "position = " + holder.getAdapterPosition());
         }
     });
     return holder;
}
4
Daria Kirsanova

Per https://developer.Android.com/topic/performance/vitals/render , onBindViewHolder devrait effectuer son travail en "beaucoup moins d'une milliseconde" pour empêcher le rendu lent.

0
Bink