【问题标题】:Checkboxes in RecyclerView is unchecked when scrolled滚动时未选中 RecyclerView 中的复选框
【发布时间】:2021-06-19 03:57:22
【问题描述】:

假设我检查了一个项目的checkbox,这也会用相应项目的ID 更新database 中的ischecked 列。然后我向下滚动并返回顶部,然后最顶部的项目checkbox 自动为unchecked 并且数据库也被更新。即使假设我检查顶部的两个项目,当我向下滚动最底部的两个项目时,checkboxes 也会自动检查。我该如何解决recyclerView 中的这个问题?

// costume adapter class
public class DataBeanAdapter extends RecyclerView.Adapter<DataBeanAdapter.ViewHolder> implements View.OnCreateContextMenuListener {
    public List<dataBean> items;
    public int itemLayout;

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo contextMenuInfo) {
        menu.setHeaderTitle("Select The Action");
        menu.add(0, v.getId(), 0, "Call");//groupId, itemId, order, title
        menu.add(0, v.getId(), 0, "SMS");
    }

    public DataBeanAdapter(List<dataBean> items, int itemLayout) {
        this.items = items;
        this.itemLayout = itemLayout;
    }

    @Override
    public DataBeanAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        // On loading the activity recyclerview, setting properties to each list items

        // fetch id with the given item postition
        Cursor cc = myDB.getCursorByPosition(position);
        String idd = cc.getString(cc.getColumnIndex("_id"));


        String taskval = myDB.getTaskValue(idd);

        dataBean item = items.get(position); // trash
        holder.tasks.setText(taskval.trim());

        String iscGet = myDB.getischecked(idd); // false by default

        if (items.get(position).getChecked() == true) {
            holder.isc.setChecked(true);
            items.get(position).setChecked(true);
        } else if (items.get(position).getChecked() == false) {
            holder.isc.setChecked(false);
            items.get(position).setChecked(false);
        } else {
            //leave
        }


        // set inflated checkbox to null
        //   holder.isc.setOnCheckedChangeListener(null);
        //  holder.isc.setChecked(myAdapter.items.contains(items.get(position)));

        holder.isc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                Boolean ischecked = items.get(position).getChecked();
                if (ischecked == false) {
                    Cursor cx = myDB.getCursorByPosition(position);
                    String idx = cx.getString(cx.getColumnIndex("_id"));
                    myDB.updateCheck(idx);
                    items.get(position).setChecked(true);

                } else {
                    Cursor cx = myDB.getCursorByPosition(position);
                    String idx = cx.getString(cx.getColumnIndex("_id"));
                    myDB.updateunCheck(idx);
                    items.get(position).setChecked(false);
                }
            }
        });


        holder.tasks.setOnCreateContextMenuListener(MainActivity.this);
        holder.more.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Cursor c = myDB.getCursorByPosition(position);
                String id = c.getString(c.getColumnIndex("_id"));

                Intent intent = new Intent(MainActivity.this, edit.class);
                intent.putExtra("taskpos", Integer.toString(position));
                intent.putExtra("taskid", id);
                startActivity(intent);

            }
        });
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public TextView tasks;
        public CheckBox isc;
        public ImageView more;

        public ViewHolder(View itemView) {
            super(itemView);
            tasks = (TextView) itemView.findViewById(R.id.taskline);
            isc = (CheckBox) itemView.findViewById(R.id.cbox);
            more = (ImageView) itemView.findViewById(R.id.more);
            // https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable()
            //  this.setIsRecyclable(false); /// prevents checkbox misbehaving


        }

    }
}

public List<dataBean> getAllData() {
    List<dataBean> list = new ArrayList<>();
    Cursor cursor = myDB.SelectData();


    while (cursor.moveToNext()) {

        int tasks = cursor.getColumnIndex("tasklist");
        int ischecked = cursor.getColumnIndex("ischecked");
        String tasksv = cursor.getString(tasks);
        String ic = cursor.getString(ischecked);
        dataBean bean = new dataBean(tasksv, Boolean.parseBoolean(ic));
        list.add(bean);

    }

    return list;
}

【问题讨论】:

    标签: android checkbox android-recyclerview


    【解决方案1】:

    问题不好,第一个答案也不好。 RecyclerView回收组件,它是一个对象池,通过复用组件并调用onBindViewHolder来实际设置要显示的数据来避免内存分配。

    这意味着每次 recyclerview 的 ViewHolder 出现在屏幕上时,都会调用 onBindViewHolder,一遍又一遍地完成所有繁忙的工作(设置侦听器和其他东西)。 OnBindViewHolder 应该只读取数据并将其反映在持有人上,以便正确显示。

    在您的情况下,错误在于您每次设置的 OnCheckedChangeListener。当 ViewHolder 被回收时,复选框会更改以反映 dataBean 数组,并且该更改会保留在您的数据库中(插入不正确的数据,因为您的侦听器绑定到以前使用的 ViewHolder 的未知位置)。然后你设置监听器,新的、不可预知的行为必然会发生。

    您应该查看大量关于 RecyclerView 的教程,或者仔细阅读 Android SDK 的文档/手册,并尝试找出如何最好地布局您的代码。

    希望这会有所帮助:)

    【讨论】:

      【解决方案2】:

      您必须使用共享首选项或在 Sql 数据库中保存复选框的状态。 注意:共享偏好是最好的解决方案。 搜索如何使用共享首选项保存复选框状态。

      【讨论】:

        【解决方案3】:

        我通过覆盖适配器类中的两个方法解决了这个问题

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

        还修改了它直接检查和取消选中项目的条件,每次视图被回收时都从数据库中检索,而不是从 dataBean 中获取选中状态。

        if (iscGet.equals("true")) {
                    holder.isc.setChecked(true);
                    items.get(position).setChecked(true);
                } else if (iscGet.equals("false")) {
                    holder.isc.setChecked(false);
                    items.get(position).setChecked(false);
                } else {
                    //leave
                }
        

        【讨论】:

          【解决方案4】:

          如果您在 recylerview 中的项目数量较少,您可以使用上述答案以使其完美运行

          recycler_filter.setItemViewCacheSize(100000);
          

          【讨论】:

            【解决方案5】:

            覆盖适配器内部的方法

            @Override public long getItemId(int position) { return ItemList.get(position).getId();}
            

            每个项目的唯一 ID,可能来自数据库。 使用 OnClickListener 代替 OnCheckChangedListener

            holder.binding.alarmChk.setOnClickListener(view -> {
                holder.binding.alarmChk.toggle();
                if (holder.binding.alarmChk.isChecked()) {
                    //do something
                    Log.d("OnCheckedChange", "false " + getItemId(position));
                    holder.binding.alarmChk.setChecked(false);
                } else {
                    //do something
                    Log.d("OnCheckedChange", "false " + getItemId(position));
                    holder.binding.alarmChk.setChecked(true);
               }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2021-12-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多