如何在回收站视图android中选择多个项目?

Posted

技术标签:

【中文标题】如何在回收站视图android中选择多个项目?【英文标题】:How to select multiple items in recycler view android? 【发布时间】:2021-09-24 03:52:35 【问题描述】:

我想在回收站视图中选择多个项目,当它被选中时,我想将该项目的复选框设置为可见性。所以,我可以使用接口设置onlongClickListner 并在片段中处理onLongClick 事件。

每当用户长按任何项目时,应用程序的onCLick 逻辑都会更改。在长按应用程序之前在另一个活动中打开该项目,但是在长按onClick 后,逻辑被更改并且可以设置任何我想要的。我想在长按后选中与该项目对应的复选框。并希望从加载到回收站视图中的arrayList 添加它。

片段

...
@Override
    public void onclick(int position) 
        if (!isSelectionMode) 
            Intent intent = new Intent(getActivity(), FullPhoto.class);
            intent.putExtra("uri", arrayList.get(position).getUri());
            startActivity(intent);
        
    

            //Support fun to turn selectionMode on, onLongClick event.

    @Override
    public void onLongClick() 
        isSelectionMode = true;
    
...

适配器

...

public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener 


        private final ImageView img;
        public CheckBox selection;
        OnImageClickListner listner;
        OnImageLongClickListener longClickListener;
        public MyViewHolder(@NonNull View itemView, OnImageClickListner listner,OnImageLongClickListener longClickListener) 
            super(itemView);
            this.listner = listner;
            this.longClickListener = longClickListener;
            itemView.setOnLongClickListener(this);          //onLongClickListener is set to all of the RecyclerView Items for once rather than setting on each item in BindViewHolder for repeated times
            itemView.setOnClickListener(this);          //onClickListener is set to all of the RecyclerView Items for once rather than setting on each item in BindViewHolder for repeated times
            img = itemView.findViewById(R.id.img);
            selection = itemView.findViewById(R.id.checkbox);

        

        @Override
        public void onClick(View v) 
            listner.onclick(getAdapterPosition());            //Returning the current clicked position
        

        @Override
        public boolean onLongClick(View v) 
            longClickListener.onLongClick();          
            return  true;
        
    
// inner class ends


    public  interface  OnImageClickListner         //Interface to generate call back when user clicked an image.
         void onclick(int position);
    

    public interface OnImageLongClickListener          //Interface to generate call back when user long clicked an image.
        void onLongClick();
    

...

在这种情况下,我无法理解如何实现选择跟踪器。我可以使用getAdapterPosition() 获取适配器位置,然后我可以从该索引上的arrayList 中删除元素。但是,我想突出显示position 处的复选框。在这种情况下,我无法实现代码。

我尝试过的事情

我确实尝试从onLongClick(View v) 传递View v,然后将selection 复选框传递给onCLick() 事件。但是,它没有用。

我想从回收站视图中选择项目并将所选项目的可见性设置为VISIBLE

------ 更新 ------

我现在可以借助事件方法中的少量编辑将复选框的可见性设置为可见。

片段

@Override
    public void onclick(int position, CheckBox selection) 
        if (!isSelectionMode) 
            Intent intent = new Intent(getActivity(), FullPhoto.class);
            intent.putExtra("uri", arrayList.get(position).getUri());
            startActivity(intent);
        
        else
        
            selection.setVisibility(View.VISIBLE);
            selection.setChecked(true);
        
    

其中selection 是从适配器的MyViewHolder 类传递的复选框。但是,由于回收者视图的性质,我得到了双重选择。还有一个奇怪的问题是,如果我向下滚动选择项目后,选择会随机更改。

如您所见,在这张图片中,我只选择了 4 张图片,但是当我向下滚动时,其他图片也被选择了,当我再次向上滚动时,它弄乱了我选择的项目。

照片适配器

public class PhotosAdapter extends RecyclerView.Adapter<PhotosAdapter.MyViewHolder> 


    public Context context;
    ArrayList<ImageModel> arrayList;
    Activity activity;
    OnImageClickListner listener;
    OnImageLongClickListener longClickListener;

    /*===============================================================   CONSTRUCTOR   ===============================================================*/

    public PhotosAdapter(Context context, ArrayList<ImageModel> arrayList, Activity activity, OnImageClickListner listner, OnImageLongClickListener longClickListener) 
        this.context = context;
        this.arrayList = arrayList;
        this.activity = activity;
        this.listener = listner;
        this.longClickListener = longClickListener;

    

    /*===============================================================   OVERRIDDEN METHODS   ===============================================================*/

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)            //This methods returns single_view.xml as a view for RecyclerView.
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_view, parent, false);
        return new MyViewHolder(view, listener, longClickListener);
    

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position)           //Binding the uris with each view depending upon the position of current view.


        activity.runOnUiThread(() -> GlideApp.with(context)
                .load(Uri.parse(arrayList.get(position).getUri()))
                .apply(RequestOptions.overrideOf(150, 150))          //It overrides the value of original image and reduces it to the visible thumbnail size.
                .diskCacheStrategy(DiskCacheStrategy.RESOURCE)          //Then it caches the reduced size thumbnail for faster loading speed.
                .into(holder.img));
    

    @Override
    public int getItemCount() 
        return arrayList.size();
    


    /*===============================================================   INNER VIEW HOLDER CLASS   ===============================================================*/

    public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener 


        private final ImageView img;
        public CheckBox selection;
        OnImageClickListner listener;
        OnImageLongClickListener longClickListener;

        public final SparseBooleanArray selectedItems;              ///////////////////////////////// ADDED LINE /////////////////////////////////

        public MyViewHolder(@NonNull View itemView, OnImageClickListner listener, OnImageLongClickListener longClickListener) 
            super(itemView);
            this.listener = listener;
            this.longClickListener = longClickListener;
            itemView.setOnLongClickListener(this);          //onLongClickListener is set to all of the RecyclerView Items for once rather than setting on each item in BindViewHolder for repeated times
            itemView.setOnClickListener(this);          //onClickListener is set to all of the RecyclerView Items for once rather than setting on each item in BindViewHolder for repeated times
            img = itemView.findViewById(R.id.img);
            selection = itemView.findViewById(R.id.checkbox);

            selectedItems = new SparseBooleanArray();             ///////////////////////////////// ADDED LINE /////////////////////////////////
        



        @Override
        public void onClick(View v) 

            listener.onclick(getAdapterPosition(), selection);            //Returning the current clicked position and selection checkbox to the implemented method.
        

        @Override
        public boolean onLongClick(View v) 
            longClickListener.onLongClick(getAdapterPosition(), v);          //Returning the current clicked position and view to the implemented method.
            return true;
        



        //////////////////////////////////////////////////////////////////////////// ADDED LINES ////////////////////////////////////////////////////////////////////////////



        boolean isSelected(int position) 
            return getSelectedItems().contains(position);
        

        public void toggleSelection(int position) 


            if (selectedItems.get(position, false)) 
                selectedItems.delete(position);
             else 

                selectedItems.put(position, true);


            
            notifyItemChanged(position);
        

        public void selectAll() 
            for (int i = 0; i < getItemCount(); i++) 
                if (!(selectedItems.get(i, false))) 
                    selectedItems.put(i, true);
                
                notifyItemChanged(i);
            
            notifyDataSetChanged();
        

        public void clearSelection() 
            List<Integer> selection = getSelectedItems();
            selectedItems.clear();
            for (Integer i : selection) 
                notifyItemChanged(i);
            
        

        public int getSelectedItemCount() 
            return selectedItems.size();
        

        public List<Integer> getSelectedItems() 
            List<Integer> items = new ArrayList<>(selectedItems.size());
            for (int i = 0; i < selectedItems.size(); ++i) 
                items.add(selectedItems.keyAt(i));
            
            return items;
        





           //INNER CLASS ENDS

    /*===============================================================   INTERFACES   ===============================================================*/

    public interface OnImageClickListner          //Interface to generate call back when user clicked an image.
        void onclick(int position, CheckBox selection);
    

    public interface OnImageLongClickListener           //Interface to generate call back when user long clicked an image.
        void onLongClick(int position, View v);
    



【问题讨论】:

code.tutsplus.com/tutorials/… 试一试 ***.com/questions/36369913/… 您想要多选项目吗? @AnantaRaha 是的,但是,我已经实现了接口。我见过很多例子,其中开发人员在onBindViewHolder 中设置onClickListeneronLongClickListner,但是我发现如果用户有大量照片,这可能会导致问题。因此,我成功实现了onClickListeneronLongClickListner,但是无法设置选中项目对应的复选框的可见性。 【参考方案1】:

这就是导致双重或多重选择的原因。 Recyclerview 以这种方式工作,即视图被回收。

因此,每次视图膨胀时,您都必须隐藏、选中或取消选中每个项目。 因此,在 onbindViewholder 中,您必须根据您的情况将 setChecked() 设置为 true 或 false。

我解决这个问题的方法是: 不要将视图传递给父片段,而是以这种方式将检查逻辑保留在适配器中:

if (isItemSelected)
          selection.setChecked(true);
else
           selection.setChecked(false);

这样做,就会有完美的检查和取消检查。

-- 更新--

创建一个Selectable适配器类,提供isSelected()方法如下

可选适配器

public abstract class SelectableAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> 
private static final String TAG = SelectableAdapter.class.getSimpleName();

private final SparseBooleanArray selectedItems;

SelectableAdapter() 

    selectedItems = new SparseBooleanArray();



boolean isSelected(int position) 
    return getSelectedItems().contains(position);


public void toggleSelection(int position) 

    
    if (selectedItems.get(position, false)) 
        selectedItems.delete(position);
     else 

            selectedItems.put(position, true);
        

    
    notifyItemChanged(position);


public void selectAll() 
    for (int i = 0; i < getItemCount(); i++) 
        if (!(selectedItems.get(i, false))) 
            selectedItems.put(i, true);
        
        notifyItemChanged(i);
    
    notifyDataSetChanged();


public void clearSelection() 
    List<Integer> selection = getSelectedItems();
    selectedItems.clear();
    for (Integer i : selection) 
        notifyItemChanged(i);
    


public int getSelectedItemCount() 
    return selectedItems.size();


public List<Integer> getSelectedItems() 
    List<Integer> items = new ArrayList<>(selectedItems.size());
    for (int i = 0; i < selectedItems.size(); ++i) 
        items.add(selectedItems.keyAt(i));
    
    return items;

让您的适配器扩展 selectableAdapter,如下所示:

适配器类

public class RecyclerViewAdapter extends SelectableAdapter<RclAdapter.ViewHolder> 

在片段上使用toggleSelection 将位置设置为选中或未选中。

@Override
    public void onclick(int position) 
        if (!isSelectionMode) 
            Intent intent = new Intent(getActivity(), FullPhoto.class);
            intent.putExtra("uri", arrayList.get(position).getUri());
            startActivity(intent);
        
        else
        
// Use the adapter instance here
            adapter.toggleSelection(position);
    
        
    

记住toggleSelection 通知适配器并调用onBindViewHolder

在适配器中:onBindViewHolder 实现选择逻辑以显示和隐藏复选框。 如果你只设置为View.VISIBLE,没有设置为View.GONE,没有选中的,还是会出现同样的问题。

【讨论】:

好的。这是有道理的...但是,我仍然不明白我将如何从片段中控制isItemSelected...我可以创建一个方法,例如设置truefalse 然后我会调用它片段......但是,如何检查特定索引处的特定照片?这部分我无法理解...如果可以的话,请用一些代码进一步解释一下... 请通过PhotosAdapter 我更新了我的问题。由于Adapter 类已经在扩展内部类,我不能让它扩展其他类。所以,我把你提到的代码放在了内部类中。但是,Inner 类是静态的,android studio 坚持如果我把它设为非静态,它应该是静态的,所以方法 getItemCount()notifyDataSetChanged() 方法显示错误。 如果您仔细查看我的代码,您会发现您实际上可以使用它,也许我没有提到您应该如何扩展主适配器上的可选适配器:这就是 @987654342 @ .以这种方式修改它,因为您当前正在扩展的内容在可选适配器中进行了扩展。 您的上一条评论已被删除之类的。 我只是问你是否使用任何社交媒体。然后我认为它可能感觉不好。所以我删除了。

以上是关于如何在回收站视图android中选择多个项目?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 android 中的回收器视图运行 for 循环?

Android Firestore 过滤器回收视图

如何从回收站视图 android 中传递一个 url onclick 项目并 onclick 获取位置 dipaly 在另一个活动中的 url

在 Android 回收站视图中重新排序项目后,位置未更新

如何在主要活动中使用按钮 onclicklistener 进行回收视图项目

如何在回收站视图中有多个视图