为什么在RecyclerView.Adapter的onBindViewHolder方法内添加OnClickListener被认为是不好的做法?

15 浏览
0 Comments

为什么在RecyclerView.Adapter的onBindViewHolder方法内添加OnClickListener被认为是不好的做法?

我有以下代码用于一个RecyclerView.Adapter类,它运行得很好:\n

public class MyAdapter extends RecyclerView.Adapter {
    private List items;
    private int itemLayout;
    
    public MyAdapter(List 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);
        }
    }
}

\n然而,我认为在onBindViewHolder方法中实现OnClickListener是不好的实践。为什么这是不好的实践,有什么更好的替代方法?

0
0 Comments

在RecyclerView.Adapter的onBindViewHolder方法中添加OnClickListener被认为是一种不良做法的原因是,onBindViewHolder方法会在每次新的item滚动进入视图或其数据发生更改时被调用。为了避免降低滚动速度,应该避免在onBindViewHolder方法中进行任何昂贵的操作。相比之下,在onCreateViewHolder方法中进行这些操作的影响要小得多。因此,通常最好在onCreateViewHolder方法中创建像OnClickListener这样的对象,以便它们每个ViewHolder对象只发生一次。您可以在监听器中调用getLayoutPosition()方法来获取当前位置,而不是使用onBindViewHolder()方法提供的position参数。

解决这个问题的方法是将OnClickListener移到onCreateViewHolder方法中。这样,每个ViewHolder对象只会创建一个OnClickListener对象,而不会在每次数据更改时都创建一个新的OnClickListener对象。这样可以避免在滚动时产生额外的开销,提高滚动的性能。

以下是一种可能的解决方法:

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // inflate the layout and create a new ViewHolder
    View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
    ViewHolder viewHolder = new ViewHolder(itemView);
    
    // create an OnClickListener for the ViewHolder
    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // handle the click event
            int position = viewHolder.getLayoutPosition();
            // do something with the position
        }
    });
    
    return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    // bind data to the ViewHolder
    // ...
}

0
0 Comments

为什么把OnClickListener放在RecyclerView.Adapter的onBindViewHolder方法中被认为是不良实践?

问题出现的原因是每次绑定视图和尚未被看到的对象时,都会调用onBindViewHolder方法,并且每次都会添加一个新的监听器。

解决方法是在onCreateViewHolder方法中附加点击监听器。

示例代码如下:

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() {
         public void onClick(View v) {
             Log.d(TAG, "position = " + holder.getAdapterPosition());
         }
     });
     return holder;
}

如果我向Activity发送位置和特定行的对象来执行CRUD操作,那么getAdapterPosition()是最好的使用方式吗?因为我使用getLayoutPosition()时,它仍然有效!

很好的答案。顺便提醒一下,要注意将getAdapterPosition()放在onClick()方法内部,而不是监听器外部,否则位置将为-1。这是因为当调用onCreateViewHolder()时,viewholder还没有附加到视图上;而当viewholder被点击时,viewholder必须已经附加到视图上,因此它才有效。此外,getAdapterPosition()将被弃用。

0
0 Comments

为什么在RecyclerView.Adapter的onBindViewHolder中添加OnClickListener被认为是不良实践?

将点击逻辑处理放在ViewHolder中更好的原因是它允许更明确的点击监听器。正如Commonsware书中所述:

在ListView行中,可点击的小部件(如RatingBar)一直与行本身的点击事件发生冲突。在某些情况下,获取可点击的行以及可点击的行内容会变得有点棘手。使用RecyclerView,您可以更明确地控制这种处理方式...因为您是设置所有on-click处理逻辑的人。

通过使用ViewHolder模型,您可以在RecyclerView中获得比在ListView中更多的点击处理优势。我在一篇博客文章中对此进行了比较- https://androidessence.com/recyclerview-vs-listview

至于为什么在ViewHolder中而不是在onBindViewHolder()中更好,那是因为onBindViewHolder()对每个项目都会调用,并且在ViewHolder构造函数中设置点击监听器是一种不必要的重复选项。然后,如果您的点击响应取决于所点击项的位置,您可以直接从ViewHolder中调用getAdapterPosition()。这里有另一个我给出的答案,演示了如何在ViewHolder类中使用OnClickListenerHere

为了避免不必要的点击监听器设置,我们明白了! 但是,我们可以按照Brucelet的建议将其实现在onCreateViewHolder()中吗(请参见下面的答案)。

我想这会产生相同的效果,因为onCreateViewHolder()只会被调用一次(每个ViewHolder),所以您是将其实现在ViewHolder构造函数中还是在onCreateViewHolder()中都取决于您的个人喜好。我养成了将其放在VH中的习惯,但您应该选择您认为最可读且能帮助您在将来理解的方式。只是出于性能原因,像brucelet建议的那样避免使用onBindViewHolder()

我倾向于在onCreateViewHolder()中进行操作,而不是在ViewHolder构造函数中,这样我可以使我的ViewHolder类成为static,并且不需要将对适配器的引用传递给ViewHolder。但这在很大程度上是样式选择,因为onCreateViewHolder()new ViewHolder()之间应该有一一对应的对应关系。

您不需要在viewholder中传递对适配器的引用吗?您可以从ViewHolder内部调用getAdapterPosition()。查看我链接的答案。除非我误解了您的意思?

感谢提醒!我之前重写了这篇博客。我已经更新了链接。 🙂

如果在VH中处理点击,那么如何实现选择?在适配器中处理选择和选择列表似乎更容易。

我在onBindViewHolderviewHolder上测试了我的recyclerView的5个按钮点击监听器。但是在性能、CPU使用率或内存使用方面从未看到任何变化。在onBindViewHolder上使用按钮点击监听器是否是一个好方法?

0