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

Posted

技术标签:

【中文标题】RecyclerView ambiguos setVisibility函数,点击一个视图影响多个视图【英文标题】:RecyclerView ambiguos setVisibility function, clicking on one view affects multiple views 【发布时间】:2015-08-15 12:58:47 【问题描述】:

This is the project我想跑。这是我在 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 项的影响。只有背景颜色的变化得到了反映,而不是文本的变化。我该如何阻止这种情况?

我一直在尝试解决这个问题,如果我的问题不清楚,请下载项目并尝试执行代码以理解我的确切意思。

【问题讨论】:

如果您喜欢这个问题的描述性,请点赞。 【参考方案1】:

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

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

解决问题

【讨论】:

这实际上也将打破拥有 RecyclerView 的全部意义。这将禁用所有视图重用。您基本上是在告诉 RecyclerView 列表中的每个项目都应该有自己的视图单元格,这些单元格永远不会被回收。因此,如果您将图像加载到这些 RecyclerView 单元格中,您的内存使用量将达到顶峰。【参考方案2】:

这是因为视图被回收和重用。

因此,当视图被回收时,如果您不再更改它们,它会保留“旧”视图的属性。因此,当您向下滚动到数字 12 时,用于保存数字 1 的视图被回收(因为它不再在屏幕上看到),并用于创建数字 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;
 

【讨论】:

我了解您的解决方案。但是为什么 setText 和 setBackgroundColor 的工作方式不同呢?为什么 setText 的工作方式就像我们想要的一样? 阿门!感谢您的精彩解释:) 我将值保存在 pojo 类中。它正在被保存。但是当我滚动 pojo 类 bool 变量时也会被删除。我滚动后它设置为 false (默认)。请提出建议。【参考方案3】:

您似乎对通过调用 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();


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

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

【讨论】:

【参考方案4】:

我遇到了类似的问题(在多个列表元素上发生了数字更改,而不仅仅是一个)。我认为这是因为回收视图的工作原理,我能够通过将我计划更改的所有内容设置为我想要的默认值来修复它。

IE:如果您想将背景更改为蓝色,则在加载列表时将那些不应该为蓝色的设置设置为灰色(或您希望的默认设置)。

所以这里:

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

你想指定默认值

【讨论】:

【参考方案5】:

如果你知道项目的数量并且它是固定的,你可以使用

setItemViewCacheSize( numItems)

这将解决它,因为它将在该编号之后开始重复使用项目,但是,您将失去 recyclerview 的所有好处。

【讨论】:

【参考方案6】:

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

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);
        
    );


【讨论】:

以上是关于RecyclerView ambiguos setVisibility函数,点击一个视图影响多个视图的主要内容,如果未能解决你的问题,请参考以下文章

recyclerview

RecyclerView.ViewHolder 的 setIsRecyclable() 函数

android 怎么监听recyclerview是不是获取焦点

Android kotlin 用RecyclerView快速实现类似于旧版GridView的功能(附源码)

Android kotlin 用RecyclerView快速实现类似于旧版GridView的功能(附源码)

为什么当我在适配器中调用编辑项目时,我的Recyclerview项目会被克隆?