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

时间:2015-06-01 22:08:41

标签: android listview textview android-imageview android-recyclerview

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的点击的影响。仅反映了背景颜色变化,而不是文本更改。我该如何阻止它?

enter image description here

enter image description here

我一直试图解决这个问题很长时间,请下载项目并尝试执行代码以了解我的意思,如果我的问题不明确的话。

6 个答案:

答案 0 :(得分:11)

这是因为视图被回收并重复使用。

因此,当视图被回收时,如果您不再次更改它,它将保留“旧”视图的属性。因此,当您向下滚动到数字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类中添加了一个“clicked”布尔值。

public class MyPojo {

    String title;
    String desc;
    int image;
    boolean clicked;
 }

答案 1 :(得分:7)

只需在getItemCount方法

之后在适配器类中添加一个方法
@Override
    public int getItemViewType(int position) {
        return position;
    }

它将解决问题

答案 2 :(得分:2)

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

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对象或其他地方保持这种状态应该相对容易实现。

答案 3 :(得分:0)

我有一个类似的问题,(在多个列表元素上有一个数字更改而不是一个)。我假设它是因为循环视图的工作方式,我能够通过设置我计划改变为我想要的任何内容来修复它。

IE:如果你想将背景更改为蓝色,当你加载列表时,那些不应该是蓝色到灰色的(或任何你想要的默认值)。

所以这里:

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

您想指定默认值

答案 4 :(得分:0)

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

setItemViewCacheSize( numItems)

这将解决该问题,因为它将在该数字之后开始重复使用项目,尽管您将失去recyclerview的所有好处。

答案 5 :(得分:-1)

这里尝试使用此适配器:

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

}

}