RecyclerView ambiguos setVisibility函数,单击一个视图会影响多个视图

这是我正在尝试运行的项目 。 这里是我从RecyclerView.Adapter类的onBindViewHolder的代码

@Override public void onBindViewHolder(ViewHolder holder, final int position) { TextView title = (TextView) holder.view.findViewById(R.id.title); final TextView desc = (TextView) holder.view.findViewById(R.id.desc); final ImageView imageView = (ImageView) holder.view.findViewById(R.id.imageView); title.setText(pojos.get(position).getTitle()); desc.setText(pojos.get(position).getDesc()); imageView.setImageResource(pojos.get(position).getImage()); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { desc.setText("clicked"); desc.setBackgroundColor(Color.BLUE); imageView.setImageResource(R.drawable.heart_red); } }); } 

列表加载正常,当imageView的onclicklistener被调用时,问题发生。

 desc.setText("clicked"); 

上面的这行代码在点击它的列表项中进行更改。 但

  desc.setBackgroundColor(Color.BLUE); 

当这行被执行时,这个改变反映在列表上的多个项目中。 出了什么问题? 在下面的图片中,我点击了项目0,文字变成“点击”,颜色被设置。 但是当我向下滚动时,项目12也受到了我对项目0的点击的影响。只有背景颜色变化反映了,而不是文本变化。 我该如何阻止?

在这里输入图像说明

在这里输入图像说明

我一直在努力解决这个问题,请好好下载这个项目,然后尝试执行这个代码来明白我的意思,如果我的问题不清楚的话。

Related of "RecyclerView ambiguos setVisibility函数,单击一个视图会影响多个视图"

发生这种情况是因为视图被回收并重新使用。

所以当视图被回收的时候,如果你不改变它们,它将保留“旧”视图的属性。 所以,当你向下滚动到12号时,用来保存数字1的视图被回收(因为它不能在屏幕上看到),并被用来创build数字12.这就是为什么蓝色是数字12。

例如,当项目被点击时,您需要将“点击”值保存到您的POJO对象中。 然后,在绘制项目时,检查该值并根据该值设置正确的图像/背景颜色。

我已经在下面的代码中做了这个,所以它应该给你一个粗略的想法:

 @Override public void onBindViewHolder(ViewHolder holder, final int position) { TextView title = (TextView) holder.view.findViewById(R.id.title); final TextView desc = (TextView) holder.view.findViewById(R.id.desc); final ImageView imageView = (ImageView) holder.view.findViewById(R.id.imageView); final MyPojo pojo = pojos.get(position); title.setText(pojo.getTitle()); if(!pojo.clicked) { desc.setText(pojo.getDesc()); imageView.setImageResource(pojo.getImage()); desc.setBackgroundColor(Color.argb(0,0,0,0)); } else { desc.setText("clicked"); desc.setBackgroundColor(Color.BLUE); imageView.setImageResource(R.drawable.heart_red); } imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { pojo.clicked = true; desc.setText("clicked"); desc.setBackgroundColor(Color.BLUE); imageView.setImageResource(R.drawable.heart_red); } }); } 

我已经添加了一个“点击”布尔MyPojo类。

 public class MyPojo { String title; String desc; int image; boolean clicked; } 

只需在getItemCount方法之后的适配器类中添加一个方法即可

 @Override public int getItemViewType(int position) { return position; } 

它会解决问题

看起来,您通过调用onBindViewHolder中的findViewById来使用RecyclerView时感到困惑。 这些昂贵的查找应该在onCreateViewHolder中进行,您可以在其中查找所有视图并将其引用保存到自定义视图持有者。 我继续在github回购中查看你的代码,并提出以下更改:

 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private ArrayList<MyPojo> pojos; // Provide a reference to the views for each data item // Complex data items may need more than one view per item, and // you provide access to all the views for a data item in a view holder public static class ViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case public TextView title; public TextView desc; public ImageView imageView; public ViewHolder(View v) { super(v); // all expensive findViewById lookups happen in ViewHolder constructor, // which is called only when onCreateViewHolder is called this.title = (TextView) v.findViewById(R.id.title); this.desc = (TextView) v.findViewById(R.id.desc); this.imageView = (ImageView) v.findViewById(R.id.imageView); } } // Provide a suitable constructor (depends on the kind of dataset) public MyAdapter(ArrayList<MyPojo> pojos) { this.pojos = pojos; } // Create new views (invoked by the layout manager) @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.row, parent, false); // set the view's size, margins, paddings and layout parameters ViewHolder vh = new ViewHolder(v); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, final int position) { // this callback will be constantly called during scrolling // therefore, to make it smooth, we should not make any expensive operations here // - get element from your dataset at this position // - replace the contents of the view with that element holder.title.setText(pojos.get(position).getTitle()); holder.desc.setText(pojos.get(position).getDesc()); holder.imageView.setImageResource(pojos.get(position).getImage()); // you'll need to implement this function based on the way you decide to save clicked state for each clicked view if(isClickedState(position)) { holder.imageView.setImageResource(R.drawable.heart_red); } else { // provide some default background holder.imageView.setImageResource(R.drawable.default); } holder.imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // you'll need to implement this function to save clicked position saveClickForPosition(position) imageView.setImageResource(R.drawable.heart_red); } }); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return pojos.size(); } } 

这应该是您的debugging的起点,因为遵循此模式将保证正确的回收。

正如在另一个答案中也提到的那样,您需要记住每个项目的点击状态,并且将这个状态保持在MyPojo对象或其他地方应该相对容易完成。

我有一个类似的问题,(多个列表元素,而不是只有一个数字更改)。 我假设它是因为回收视图是如何工作的,我可以通过设置我打算改变的任何东西来解决这个问题。

IE:如果你想改变背景为蓝色,当你加载列表中设置不应该是蓝色的灰色(或任何你想要的默认值)。

所以在这里:

 ViewHolder vh = new ViewHolder(v); return vh; 

你想指定默认值

这里尝试使用这个适配器:

 public class myAdapter extends RecyclerView.Adapter<CopyOfConversationAdapter.ViewHolder> { private ArrayList<conversationItem> pojos; // inner class to hold a reference to each item of RecyclerView public static class ViewHolder extends RecyclerView.ViewHolder { TextView title; TextView desc; ImageView imageView; public ViewHolder(View itemLayoutView) { super(itemLayoutView); title= (TextView) itemLayoutView.findViewById(R.id.title); desc= (TextView) itemLayoutView.findViewById(R.id.desc); imageView= (ImageView) itemLayoutView.findViewById(R.id.imageView); } } // Return the size of your itemsData (invoked by the layout manager) @Override public int getItemCount() { return pojos.size(); } public CopyOfConversationAdapter(Pojos[] pojos) { this.pojos = new ArrayList<conversationItem>(); this.pojos.addAll(Arrays.asList(Items)); } // Create new views (invoked by the layout manager) @Override public CopyOfConversationAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View itemLayoutView; itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.comments_item_layout_, null); ViewHolder viewHolder = new ViewHolder(itemLayoutView); return viewHolder; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder viewHolder, final int position) { // - get data from your itemsData at this position // - replace the contents of the view with that itemsData viewHolder.title.setText(pojos.get(position).getSender()); viewHolder.desc.setText(pojos.get(position).getSnippet()); viewHolder.imageView.setText(pojos.get(position).getIcon()); viewHolder.imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub desc.setText("clicked"); desc.setBackgroundColor(Color.BLUE); imageView.setImageResource(R.drawable.heart_red); } }); } 

}