Подтвердить что ты не робот

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

4b9b3361

Ответ 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 занимает много времени, убедитесь, что вы делаете минимальный работать в вашем коде привязки.