在 Android RecyclerView 中实现多选

Implementing multi selection in Android RecyclerView

我需要一些关于 Multi/Single selection 的帮助。找到了我要找的东西 ,因为它很简单。 我正在使用 GridLayoutManager 我的适配器中有 90 多个项目,一个 CardView 和一个 TextView 和一个 ImageView,同时使用 [=27= 中描述的过程].

当我 select 一个或多个项目时,当我向下滚动时,其他项目 "seems" 被 select 编辑,因为背景复制了,但它们不是 select编辑。 尝试将 setOnClickListener 放置在 onBindViewHolder 以及 MyViewHolder class 中,并且在它们中我得到相同的行为。向下滚动时,其他项目似乎被 selected。 在适配器中使用 notifyItemChanged(position)notifyDataSetChanged(),但背景根本没有改变,尽管 setSelected 工作正常。 我还在 RecyclerView 设置中使用了 setHasFixedSize(true)

onBindViewHolder

@Override
public void onBindViewHolder(MyViewHolder myViewHolder, final int position) {

    PatternImages currentPattern = patternImages.get(position);
    myViewHolder.setData(currentPattern, position);
    myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            v.setSelected(!v.isSelected());
            if (v.isSelected()) {
                v.setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimaryHighLight));

            } else {
                v.setBackgroundColor(Color.WHITE);

            }
            notifyItemChanged(position);
        }
    });
}

型号

public class PatternImages {

    private int imageId, imageName;
    private boolean isSelected;

    public PatternImages(int imageId, int imageName, boolean isSelected) {

        this.imageId = imageId;
        this.imageName = imageName;
        this.isSelected = isSelected;
    }
    public int getImageId() {

        return imageId;
    }
    public void setImageId(int imageId) {

        this.imageId = imageId;
    }
    public int getImageName() {

        return imageName;
    }
    public void setImageName(int imageName) {

        this.imageName = imageName;
    }
    public boolean isSelected() {

        return isSelected;
    }
    public void setSelected(boolean selected) {

        isSelected = selected;
    }

RecyclerView 设置

 private void setUpPatternsRecyclerView() {

    RecyclerView recyclerPatternsView = (RecyclerView) findViewById(R.id.pattern_image_recycler_view);
    PatternImageAdapter adapter = new PatternImageAdapter(this, patternImages);
    recyclerPatternsView.setAdapter(adapter);
    ColumnQty columnQty = new ColumnQty(this, R.layout.item_image_pattern_cardview);
    GridLayoutManager gridLayoutManager = new GridLayoutManager(getApplicationContext(), columnQty.calculateNoOfColumns());
    recyclerPatternsView.setHasFixedSize(true);
    recyclerPatternsView.setLayoutManager(gridLayoutManager);
    recyclerPatternsView.setItemAnimator(new DefaultItemAnimator());
    recyclerPatternsView.addItemDecoration(new GridSpacing(columnQty.calculateSpacing()));

}

setData 方法

public void setData(PatternImages currentPattern, int position) {

    this.position = position;
    patternName.setText(context.getString(currentPattern.getImageName()));
    patternName.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/ElMessiri-SemiBold.ttf"));
    patternImage.setBackgroundResource(currentPattern.getImageId());
    if (position == 0 || position == 1) {
        animationDrawable = (AnimationDrawable) patternImage.getBackground();
        animationDrawable.start();
    }


}

A RecyclerView 顾名思义,回收视图。这意味着一旦视图滚出屏幕,就可以重新使用它。

在重新使用视图之前,它仍然包含上次使用时的所有设置。例如,如果它包含 TextView,则 TextView 的文本 属性 仍将设置为上次显示时的文本。

某些项目 "seem" 被选中的原因是您选择的滚动到屏幕外的视图现在被重新使用,而您没有取消选择它们。

在您的 OnBindViewHolder 方法中,您需要 "reset" 将所有视图恢复为默认值。在这种情况下,"turn off" 无论您使用何种方法使视图显示为已选中。

例如:

@Override
public void onBindViewHolder(MyViewHolder myViewHolder, final int position) {

    final PatternImages currentPattern = patternImages.get(position);

    myViewHolder.setData(currentPattern, position);
    myViewHolder.itemView.setBackgroundColor(currentPattern.isSelected() ?R.color.Red: R.color.WHITE); // choose your colors

    myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            currentPattern.setSelected(!currentPattern.isSelected())
            notifyItemChanged(position);
        }
    });
}

本质上,每次绑定时,根据模型中的相关属性将背景颜色设置为选中或未选中状态。

最近,我在研究 recyclerview 多选,所以你可以先尝试初始化 sparseboolean,boolean 一个 int 像这样:

private SparseBooleanArray storeChecked = new SparseBooleanArray();
private boolean isMultiselect;
private int itemSelected;

在 bindViewHolder 上添加这个

holder.view.setbackground(storechecked.get(position) ? Color.White : Color.Black)

那么, 实现 onLongClickListener。 长按 添加:

if(!ismultiSelect){
  storechecked.put(getAdapterPosition(), true);
  notifyDataSetChanged(getAdapterPosition());
  triggerOnLongClickListener(++itemSelected); // using listerner i've transfer position to fragment for actionmode selected count

}


在此之后,在 onClick 中执行此操作:

if(ismultiSelect){
   boolean tof = storechecked.get(getAdapterPosition());
           if (tof){
                triggerOnItemClickListener(--itemSelected, v); // transfer position to update unselected 
                storeChecked.delete(position);// delete position of unselected position in the fragment
            }else {
                triggerOnItemClickListener(++itemSelected, v);
             // transfer position to update selected  position in the fragment
        }
    } 

 **Other methods in adapter**

   //clear on actionmode close
   public void exitMultiselectMode() {
    isMultiselect = false;
    itemSelected = 0;
    storeChecked.clear();
    notifyDataSetChanged();
 }

   // get all selected position
   public List<Integer> getSelectedItems() {
     List<Integer> items = new ArrayList<>(storeChecked.size());
     for (int i = 0; i < storeChecked.size(); ++i) {
         items.add(storeChecked.keyAt(i));
     }
     return items;
  }

尽量在模型中保持状态在视图中,并将模型绑定到视图中onBindViewHolder

我尝试了@Kuffs 解决方案,我发布了我的最终代码以供参考。

onBindViewHolder

@Override
public void onBindViewHolder(final MyViewHolder myViewHolder, final int position) {

    final PatternImages currentPattern = patternImages.get(position);
    myViewHolder.setData(currentPattern, position);
    myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            currentPattern.setSelected(!currentPattern.isSelected());

            notifyItemChanged(position);
        }
    });

}

setData() 方法

public void setData(PatternImages currentPattern, int position) {

    this.position = position;
    patternName.setText(context.getString(currentPattern.getImageName()));
    patternName.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/ElMessiri-SemiBold.ttf"));
    patternImage.setBackgroundResource(currentPattern.getImageId());
    if (position == 0 || position == 1) {
        animationDrawable = (AnimationDrawable) patternImage.getBackground();
        animationDrawable.start();
    }
    itemView.setBackgroundColor(currentPattern.isSelected() ? ContextCompat.getColor(context, R.color.colorPrimaryHighLight) : Color.WHITE);
}