web-dev-qa-db-ja.com

RecyclerView.AdapterのonBindViewHolder内にOnClickListenerを追加するのは悪い習慣と見なされるのはなぜですか?

RecyclerView.Adapterクラスに次のコードがあり、正常に動作します。

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

        }
    }
}

ただし、OnClickListenerをonBindViewHolderメソッドに実装するのは悪い習慣だと思います。なぜこれが悪い習慣であり、より良い代替策は何ですか?

63
Sujit Yadav

ViewHolder内でクリックロジックを処理する方が良い理由は、より明示的なクリックリスナーを許可するためです。 Commonswareブックに記載されているとおり:

ListView行のRatingBarなどのクリック可能なウィジェットは、行自体のクリックイベントと長い間競合していました。クリック可能な行と、クリック可能な行のコンテンツを取得することは、時々少し注意が必要です。 RecyclerViewを使用すると、この種の処理方法をより明確に制御できます。これは、クリック時のすべての処理ロジックを設定しているためです。

ViewHolderモデルを使用することで、RecyclerViewのクリック処理で、以前のListViewよりも多くの利点を得ることができます。これについては、ブログ投稿で違いを比較して書いています- https://androidessence.com/recyclerview-vs-listview

onBindViewHolder()ではなくViewHolderの方が優れている理由は、onBindViewHolder()が各アイテムごとに呼び出され、クリックリスナーの設定は、一度呼び出すことができるときに繰り返す不要なオプションだからです。 ViewHolderコンストラクターで。次に、クリックしたアイテムの位置に応じてクリックが応答する場合、ViewHolder内からgetAdapterPosition()を呼び出すだけです。 Here は、ViewHolderクラス内からOnClickListenerを使用する方法を示す別の回答です。

55
AdamMc331

onCreateViewHolder()メソッドは、各ViewHolderviewTypeが必要な最初の数回呼び出されます。 onBindViewHolder()メソッドは、新しいアイテムがスクロールして表示されるか、データが変更されるたびに呼び出されます。スクロールが遅くなる可能性があるため、onBindViewHolder()での高価な操作を避けたい。これは、onCreateViewHolder()の問題ではありません。したがって、OnClickListenerオブジェクトごとに1回だけ発生するように、onCreateViewHolder()ViewHoldersのようなものを作成することをお勧めします。 getLayoutPosition()に提供されたposition引数を取るのではなく、リスナー内でonBindViewHolder()を呼び出して現在の位置を取得できます。

13
Brucelet

表示されていないオブジェクトにビューをバインドするたびに、メソッドonBindViewHolderが呼び出されます。そして、新しいリスナーを追加するたびに。

代わりに、クリックリスナーをonCreateViewHolderにアタッチする必要があります

例:

@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;
}
10
Pavel Kozemirov

Pavel 提供 最後の1行を除く素晴らしいコード例。作成したホルダーを返却する必要があります。新しい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;
}
6
Daria Kirsanova

https://developer.Android.com/topic/performance/vitals/renderonBindViewHolderは、レンダリングが遅くならないように「1ミリ秒未満」で作業を行う必要があります。

RecyclerView:バインドに時間がかかりすぎる

バインド(つまり、onBindViewHolder(VH、int))は非常にシンプルで、最も複雑なアイテムを除くすべてのアイテムについて1ミリ秒未満で処理する必要があります。アダプタの内部アイテムデータからPOJOアイテムを取得し、ViewHolderのビューでセッターを呼び出すだけです。 RV OnBindViewに時間がかかっている場合は、バインドコードで最小限の作業を行っていることを確認してください。

1
Bink