wake-up-neo.net

Warum wird das Hinzufügen eines OnClickListener in onBindViewHolder eines RecyclerView.Adapter als schlechte Praxis betrachtet?

Ich habe den folgenden Code für eine RecyclerView.Adapter-Klasse und funktioniert einwandfrei:

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

        }
    }
}

Ich halte es jedoch für eine schlechte Praxis, den OnClickListener in der onBindViewHolder-Methode zu implementieren. Warum ist das eine schlechte Praxis und was ist eine bessere Alternative?

54
Sujit Yadav

Der Grund, warum es besser ist, mit Ihrer Klicklogik im ViewHolder umzugehen, besteht darin, dass sie explizitere Klicklistener zulässt. Wie im Commonsware-Buch ausgedrückt:

Anklickbare Widgets wie eine Ratingleiste in einer ListView-Zeile standen seit langem in Konflikt mit Klickereignissen in den Zeilen selbst. Das Anklicken von Zeilen, die angeklickt werden können, mit Zeileninhalten, die auch angeklickt werden können, ist manchmal etwas schwierig. Mit RecyclerView haben Sie eine eindeutigere Kontrolle darüber, wie diese Dinge gehandhabt werden, weil Sie die gesamte Logik für das Klicken auf einen Klick einrichten.

Durch die Verwendung des ViewHolder-Modells können Sie eine Reihe von Vorteilen für die Handhabung von Klicks in einer RecyclerView-Version erzielen, als zuvor in der ListView-Ansicht. Ich habe darüber in einem Blogbeitrag geschrieben und die Unterschiede verglichen - https://androidessence.com/Android/recyclerview-vs-listview/

Der Grund, warum es in ViewHolder besser ist als in onBindViewHolder(), liegt daran, dass onBindViewHolder() für jedes Element aufgerufen wird. Die Einstellung des Klicklisteners ist eine unnötige Option, die wiederholt werden muss, wenn Sie sie einmal in Ihrem ViewHolder-Konstruktor aufrufen können. Wenn Ihr Klick von der Position des angeklickten Elements abhängt, können Sie einfach getAdapterPosition() vom ViewHolder aus aufrufen. Hier ist eine weitere Antwort, die ich gegeben habe und die zeigt, wie Sie die OnClickListener in Ihrer ViewHolder-Klasse verwenden können.

50
AdamMc331

Die onCreateViewHolder()-Methode wird mehrfach aufgerufen, wenn eine ViewHolder von jeder viewType benötigt wird. Die onBindViewHolder()-Methode wird jedes Mal aufgerufen, wenn ein neues Element in die Ansicht verschoben wird oder wenn sich seine Daten ändern. Sie möchten teure Vorgänge in onBindViewHolder() vermeiden, da dies den Bildlauf verlangsamen kann. Dies ist weniger ein Problem in onCreateViewHolder(). Daher ist es im Allgemeinen besser, Dinge wie OnClickListeners in onCreateViewHolder() zu erstellen, sodass sie nur einmal pro ViewHolder-Objekt auftreten. Sie können getLayoutPosition() im Listener aufrufen, um die aktuelle Position abzurufen, anstatt das position-Argument an onBindViewHolder() zu übergeben. 

13
Brucelet

Die Methode onBindViewHolder wird jedes Mal aufgerufen, wenn Sie Ihre Ansicht mit einem Objekt verbinden, das gerade nicht angezeigt wurde. Und jedes Mal fügen Sie einen neuen Listener hinzu. Du solltest das tun 

@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 vorausgesetzt tolles Codebeispiel außer einer Zeile am Ende. Sie sollten den erstellten Inhaber zurückgeben. Nicht der neue 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 sollte onBindViewHolder in "weniger als einer Millisekunde" arbeiten, um langsames Rendern zu verhindern.

0
Bink