在Recylerview中实现复选框。MVVM Room ViewModel LiveData Recylerview复选框

3

我使用Room、ViewModel、LiveData和Recyclerview实现了一个MVVM模式的Android Java应用程序。

  1. Recyclerview中有一个项目列表,并且每个项目中都有一个复选框。
  2. 当启用或禁用Recyclerview项中的复选框时,通过ViewModel更新Room数据库中的模型。
  3. 为了监听每个Recyclerview项上的复选框启用或禁用事件,我已经实现了一个OnCheckedChangeListener。

对于只有1或2个项目的Recyclerview,一切正常,

但是当Recyclerview项目增加到超过10个项目时,例如>10个项目时,当我启用或禁用复选框时,Recyclerview中的其他项目也会受到影响。

例如:

如果我更改item[5](项目ID为21,启用复选框)的复选框,则也会触发其他项目的OnCheckedChangeListener,如item[1](项目ID为15,启用复选框)和item[8](项目ID为18,启用复选框)。

Logcat:

2019-04-25 11:15:33.343 9890-9890/com.hardian.mvvm.sample D/RVAdapter: setOnCheckedChange--- ID :21 isRetryEnabled; false isChecked true
2019-04-25 11:15:33.405 9890-9890/com.hardian.mvvm.sample D/RVAdapter: setOnCheckedChange--- ID :15 isRetryEnabled; false isChecked true
2019-04-25 11:15:33.415 9890-9890/com.hardian.mvvm.sample D/RVAdapter: setOnCheckedChange--- ID :18 isRetryEnabled; false isChecked true

代码:在我的StatusRecyclerViewAdapter中

 @Override
    public void onBindViewHolder(StatusViewHolder holder, int position) {
        StatusItem  statusItem = mDataSet.get(position);

        if (statusItem != null) {
            cbStatus.setChecked(statusItem.isRetryEnabled());

            cbStatus.setOnCheckedChangeListener((buttonView, isChecked) -> {
                Log.d("RVAdapter", "setOnCheckedChange--- ID :" + statusItem.getId()+ " isRetryEnabled; "+statusItem.isRetryEnabled()+" isChecked "+isChecked);
                //update the model via the activitiy's viewmodel through the onStatusCheckBoxChangeListener interface.
                if (onStatusCheckBoxChangeListener != null)
                    onStatusCheckBoxChangeListener.onStatusCheckBoxChanged(statusItem,isChecked);
            });

        }
    }

我需要解决Recyclerview回收问题的解决方案。

注意: 我已经尝试了this.setIsRecyclable(false);,它解决了这个问题,但不推荐使用,我需要找到另一种机制来避免这种情况发生。

4个回答

3
  1. 给你的复选框设置标签并从标签中获取项目。
  2. 不要使用setOnCheckedChangeListener,改用setOnClickListener。

代码:

cbStatus.setTag(statusItem);
cbStatus.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        CheckBox cb = (CheckBox) v;
        StatusItem item = (StatusItem) cb.getTag();
        if (onStatusCheckBoxChangeListener != null)
            onStatusCheckBoxChangeListener.onStatusCheckBoxChanged(item,cb.isChecked());
    }
});

哇,感谢setTag部分。不知道可以通过它在视图上设置模型类。 - Vygintas B

1
我认为你需要通过holder.getAdapterPosition()获取位置,那么这应该是正确的。
为什么?因为
请注意,与ListView不同,RecyclerView将不会调用此方法,除非数据集中的项目位置发生更改,除非项目本身无效或无法确定新位置。... 如果您稍后需要项目的位置(例如在单击侦听器中),请使用getAdapterPosition(),它将具有更新的适配器位置。
另外,在onBindViewHolder内设置onClickListener并不是最佳实践,因为每次调用onBindViewHolder时都将设置监听器。最好在onCreateViewHolder中设置,因为onCreate只在必须创建新的ViewHolder时才会调用,因此频率要低得多。

Viewholder 也会被回收吗?我的意思是每个项目都会调用所有方法。 - Vygintas B
并非每个项目都有“自己的”ViewHolder。ViewHolder在滚动时可能会获得不同的项目,因为设置新项目要比填充完整布局要便宜得多。 - Rene Ferrari
真的。此外,在访问元素时,您不需要使用 holder 实例。 - Vygintas B

0

技巧在于将复选框设置为不可点击,并手动检查。为了使其正常工作,您必须在数据列表中保存其状态,并根据CheckBox的值进行更新。

首先将CheckBox设置为不可点击。

    android:clickable="false"
    android:focusable="false"
    android:focusableInTouchMode="false"


接下来设置您的ClickListener

  holder.cbStatus.setOnClickListener(v -> {
        StatusItem  statusItem = mDataSet.get(position);

        if (cbStatus.isChecked()) {
            cbStatus.setChecked(false);
            statusItem.setChecked(false);

        } else {
            cbStatus.setChecked(true);
            statusItem.setChecked(true);
        }
 });

我明白在点击监听器中使用了而不是选择更改监听器。感谢您提供的信息。在这里使用viewmodel Livedata Room模式,数据集更新仅通过AndroidViewModel对象进行。 - Hardian

0

你必须设置一个else语句,因为视图是被回收并使用“旧”视图。

或者你可以像这样做:

    @Override
public void onBindViewHolder(StatusViewHolder holder, int position) {
    StatusItem  statusItem = mDataSet.get(position);

    youCheckBox.setChecked(false);

    if (statusItem != null) {
        cbStatus.setChecked(statusItem.isRetryEnabled());

        cbStatus.setOnCheckedChangeListener((buttonView, isChecked) -> {
            Log.d("RVAdapter", "setOnCheckedChange--- ID :" + statusItem.getId()+ " isRetryEnabled; "+statusItem.isRetryEnabled()+" isChecked "+isChecked);
            //update the model via the activitiy's viewmodel through the onStatusCheckBoxChangeListener interface.
            if (onStatusCheckBoxChangeListener != null)
                onStatusCheckBoxChangeListener.onStatusCheckBoxChanged(statusItem,isChecked);
        });

    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接