为什么在RecyclerView.Adapter的onBindViewHolder方法内添加OnClickListener被认为是不好的做法?
为什么在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是不好的实践。为什么这是不好的实践,有什么更好的替代方法?
在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 // ... }
为什么把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()将被弃用。
为什么在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类中使用OnClickListener
。Here
为了避免不必要的点击监听器设置,我们明白了! 但是,我们可以按照Brucelet的建议将其实现在onCreateViewHolder()中吗(请参见下面的答案)。
我想这会产生相同的效果,因为onCreateViewHolder()
只会被调用一次(每个ViewHolder),所以您是将其实现在ViewHolder构造函数中还是在onCreateViewHolder()
中都取决于您的个人喜好。我养成了将其放在VH中的习惯,但您应该选择您认为最可读且能帮助您在将来理解的方式。只是出于性能原因,像brucelet建议的那样避免使用onBindViewHolder()
。
我倾向于在onCreateViewHolder()
中进行操作,而不是在ViewHolder构造函数中,这样我可以使我的ViewHolder类成为static,并且不需要将对适配器的引用传递给ViewHolder。但这在很大程度上是样式选择,因为onCreateViewHolder()
和new ViewHolder()
之间应该有一一对应的对应关系。
您不需要在viewholder中传递对适配器的引用吗?您可以从ViewHolder内部调用getAdapterPosition()
。查看我链接的答案。除非我误解了您的意思?
感谢提醒!我之前重写了这篇博客。我已经更新了链接。 🙂
如果在VH中处理点击,那么如何实现选择?在适配器中处理选择和选择列表似乎更容易。
我在onBindViewHolder
和viewHolder
上测试了我的recyclerView
的5个按钮点击监听器。但是在性能、CPU使用率或内存使用方面从未看到任何变化。在onBindViewHolder上使用按钮点击监听器是否是一个好方法?