it-swarm.com.ru

Почему добавление 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. Почему это плохая практика, и что является лучшей альтернативой?

54
Sujit Yadav

Причина, по которой лучше обрабатывать логику щелчков внутри ViewHolder, заключается в том, что она позволяет более явно прослушивать щелчки. Как указано в книге Commonsware:

Кликабельные виджеты, такие как RatingBar, в строке ListView долгое время находились в конфликте с событиями нажатия на самих строках. Получение строк, по которым можно щелкнуть, с содержимым строк, по которым также можно щелкать, иногда становится немного сложнее. С RecyclerView вы более четко контролируете, как обрабатываются подобные вещи ... потому что именно вы настраиваете всю логику обработки по нажатию.

Используя модель ViewHolder, вы можете получить множество преимуществ для обработки кликов в RecyclerView, чем ранее в ListView. Я написал об этом в блоге, сравнивая различия - https://androidessence.com/Android/recyclerview-vs-listview/

Что касается того, почему это лучше в ViewHolder, а не в onBindViewHolder(), то есть потому, что onBindViewHolder() вызывается для каждого элемента, а установка прослушивателя щелчков является ненужной опцией, которую нужно повторять, когда вы можете вызвать ее один раз в конструкторе ViewHolder. Затем, если ваш щелчок отвечает, зависит от позиции нажатого элемента, вы можете просто вызвать getAdapterPosition() из ViewHolder. Здесь другой ответ, который я дал, который демонстрирует, как вы можете использовать OnClickListener из вашего класса ViewHolder.

50
AdamMc331

Метод onCreateViewHolder() будет вызываться в первые несколько раз, когда требуется ViewHolder для каждой viewType. Метод onBindViewHolder() будет вызываться каждый раз, когда новый элемент прокручивается в поле зрения или изменяется его данные. Вы хотите избежать дорогостоящих операций в onBindViewHolder(), потому что это может замедлить прокрутку. Это менее важно в onCreateViewHolder(). Таким образом, обычно лучше создавать такие вещи, как OnClickListeners в onCreateViewHolder(), чтобы они происходили только один раз для объекта ViewHolder. Вы можете вызвать getLayoutPosition() внутри слушателя, чтобы получить текущую позицию, вместо того, чтобы принимать аргумент position, предоставленный onBindViewHolder()

13
Brucelet

Метод onBindViewHolder вызывается каждый раз, когда вы связываете свое представление с объектом, который просто не был виден. И каждый раз вы будете добавлять нового слушателя. Вы должны сделать это 

@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 предоставляется отличный пример кода, за исключением одной строки в конце. Вы должны вернуть созданного владельца. Не новый пользователь (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 должен выполнять свою работу за «гораздо меньше одной миллисекунды», чтобы предотвратить медленный рендеринг.

0
Bink