Почему добавление OnClickListener внутри onBindViewHolder из RecyclerView.Adapter считается плохой практикой?
У меня есть следующий код для класса 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
. Почему эта плохая практика, и что является лучшей альтернативой?
Ответы
Ответ 1
Причина, по которой лучше обрабатывать логику кликов внутри ViewHolder, заключается в том, что она позволяет использовать более явные прослушиватели кликов. Как указано в книге Commonsware:
Кликабельные виджеты, такие как RatingBar, в строке ListView долгое время находились в конфликте с событиями нажатия на самих строках. Получение строк, по которым можно щелкнуть, с содержимым строк, по которым также можно щелкать, иногда становится немного сложнее. С RecyclerView вы более четко контролируете, как обрабатываются подобные вещи... потому что именно вы настраиваете всю логику обработки по нажатию.
Используя модель ViewHolder, вы можете получить множество преимуществ для обработки кликов в RecyclerView, чем ранее в ListView. Я написал об этом в блоге, сравнивая различия - https://androidessence.com/recyclerview-vs-listview
Что касается того, почему это лучше в ViewHolder, а не в onBindViewHolder()
, потому что onBindViewHolder()
вызывается для каждого элемента, а установка прослушивателя щелчков является ненужной опцией, которую нужно повторять, когда вы можете вызвать ее один раз в конструкторе ViewHolder, Затем, если ваш щелчок отвечает, зависит от позиции getAdapterPosition()
элемента, вы можете просто вызвать getAdapterPosition()
из ViewHolder. Вот еще один ответ, который я дал, который демонстрирует, как вы можете использовать OnClickListener
из вашего класса ViewHolder.
Ответ 2
Метод onCreateViewHolder()
будет называться первым несколько раз, когда a ViewHolder
требуется для каждого viewType
. Метод onBindViewHolder()
будет вызываться каждый раз, когда новый элемент прокручивается в представлении или изменяется его данные. Вы хотите избежать каких-либо дорогостоящих операций в onBindViewHolder()
, потому что это может замедлить вашу прокрутку. Это менее опасно в onCreateViewHolder()
. Таким образом, обычно лучше создавать такие вещи, как OnClickListener
в onCreateViewHolder()
, чтобы они выполнялись только один раз за объект ViewHolder
. Вы можете вызвать getLayoutPosition()
внутри слушателя, чтобы получить текущую позицию, вместо того, чтобы принимать аргумент position
, предоставленный onBindViewHolder()
.
Ответ 3
Метод 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;
}
Ответ 4
Павел представил отличный пример кода, кроме одной строки в конце. Вы должны вернуть созданного владельца. Не новый зритель (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;
}
Ответ 5
В https://developer.android.com/topic/performance/vitals/render, onBindViewHolder
должен выполнять свою работу "намного меньше, чем за одну миллисекунду", чтобы предотвратить медленный рендеринг.
RecyclerView: Bind taking too long
Bind (то есть onBindViewHolder (VH, int)) должен быть очень простым и займет гораздо меньше одной миллисекунды для всех, кроме самых сложных Предметы. Он просто должен взять элементы POJO из вашего внутреннего адаптера данные элемента и установщики вызовов для представлений в ViewHolder. Если RV OnBindView занимает много времени, убедитесь, что вы делаете минимальный работать в вашем коде привязки.