已选中的RadioButton回收利用

3
我正在以编程方式向我的recyclerview添加radiogroups,并且它工作得很好。但是当我检查它并滚动recyclerview时,它会失去选中的单选按钮。
我已经看到了许多解决方案的方法和示例,但我无法实现它。已经连续几天了。
如下所示,我将选中的单选按钮保存在模型中。
适配器:
@Override
public void onBindViewHolder(final NROptionLineHolder holder, int position) {

    holder.priceGroup.removeAllViews();
    holder.priceGroup.setOnCheckedChangeListener(null);

    int id = (position+1)*100;
    checklistModel = mChecklists.get(position);
    holder.packageName.setText(checklistModel.getTitle());

    for(String price : checklistModel.getQuestions()){
        RadioButton rb = new RadioButton(NROptionLineAdapter.this.context);
        rb.setId(id++);
        rb.setText(price);
        holder.priceGroup.addView(rb);
    }
    holder.priceGroup.check(checklistModel.getSelectedId());

    holder.priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            checklistModel.setSelectedId(checkedId);
            Log.d(TAG, "onCheckedChanged: " + checkedId);
        }
    });

}

持有人

    OnNROptionListener onNROptionListener;

    public NROptionLineHolder(View itemView, OnNROptionListener onNROptionListener) {
        super(itemView);

        packageName = itemView.findViewById(R.id.package_name);
        priceGroup = itemView.findViewById(R.id.price_grp);

//        priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
//            @Override
//            public void onCheckedChanged(RadioGroup radioGroup, int i) {
//
//                Log.d(TAG, "onCheckedChanged: " + radioGroup.getCheckedRadioButtonId() + " " + i);
//            }
//        });

        this.onNROptionListener = onNROptionListener;
        itemView.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        onNROptionListener.onNROptionClick(getAdapterPosition());
    }

    public interface OnNROptionListener {
        void onNROptionClick(int position);
    }
}

编辑1 - 单选按钮组

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/package_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <RadioGroup
        android:id="@+id/price_grp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/package_name"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:orientation="horizontal"/>
</android.support.constraint.ConstraintLayout>

编辑 2

根据要求,以下是来自我的 ChecklistActivity 的重要代码:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_checklist);

    intent = getIntent();
    size = intent.getIntExtra("size", 0);
    nr = intent.getIntExtra("nr", 0);

    Log.d(TAG, "Checklist Activity - Qtd Questões: " + size);
    Log.d(TAG, "Checklist Activity - NR: " + nr);

    btnSaveCheck = findViewById(R.id.btnSaveChecklist);

    mRecyclerView = findViewById(R.id.package_lst);
    setupRecycler();

    btnSaveCheck.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(getApplicationContext(), "Sucesso", Toast.LENGTH_SHORT).show();
        }
    });
}

private void setupRecycler() {

    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    mRecyclerView.setLayoutManager(layoutManager);

    setupList();

    mAdapter = new NROptionLineAdapter(data, this, getApplication());
    mRecyclerView.setAdapter(mAdapter);

}

private void setupList(){
    data = new ArrayList<>();

    class setupList extends AsyncTask<Void, Void, List<MRNrOption>> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected List<MRNrOption> doInBackground(Void... voids) {

            list = DatabaseClient
                    .getInstance(getApplicationContext())
                    .getAppDatabase()
                    .mrNrOptionDAO()
                    .loadAllByNRId(nr);
            return list;

        }

        @Override
        protected void onPostExecute(List<MRNrOption> list) {
            super.onPostExecute(list);

            List<String> priceList = new ArrayList<>();
            priceList.add("Sim");
            priceList.add("Não");
            priceList.add("Não se Aplica");

            for (int i=0; i<list.size(); i++) {
                Log.d(TAG, "NRs Activity - Adding To List: " + list.get(i).getTitle());
                data.add(new Checklist(
                        list.get(i).getTitle(),
                        priceList)
                );

                mAdapter.notifyDataSetChanged();
            }

        }
    }

    setupList lm = new setupList();
    lm.execute();
}

带RadioButton的RecyclerView

编辑3 - 重要

由于我从服务器获取所有问题,用户之前的选择会影响问题数量,所以RadioGroups和RadioButtons是以编程方式生成的,这就是我需要这样做的原因。

编辑4

GIF以增强问题可视化

编辑5 - Checklist模型类

    public class Checklist {

    String title;
    List<String> questions;
    boolean isRadioButtonAdded;
    int selectedId;

    public Checklist(String title, List<String> questions) {
        this.title = title;
        this.questions = questions;
    }

    public Checklist(){}

    public boolean getIsAdded(){
        return isRadioButtonAdded;
    }

    public void setIsAdded(boolean isAdded){
        this.isRadioButtonAdded = isAdded;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<String> getQuestions() {
        return questions;
    }

    public void setQuestions(List<String> questions) {
        this.questions = questions;
    }

    public int getSelectedId() {
        return selectedId;
    }

    public void setSelectedId(int selectedId) {
        this.selectedId = selectedId;
    }
}

你是否为单选按钮使用样式?如果是,请分享一下。 - milad salimi
@miladsalimi,请看我的编辑。 - Alan Godoi
请分享你的片段或活动。 - milad salimi
@miladsalimi 已更新 - Alan Godoi
mChecklists.get(position).setSelectedId(checkedId) 你可以尝试在onCheckedChanged中实现吗? - denizs
我已经在onBindViewHolder中获取了位置的checklistModel = mChecklists.get(position);,并在setOnCheckedChangeListener中设置了选定的id,这不是一样的吗? - Alan Godoi
6个回答

1
虽然我不确定这是否能解决您的问题,但作为优化和良好实践,您应该将侦听器附加到onCreateViewHolder而不是onBindViewHolder,这可以防止为侦听器创建多个对象。
为什么不将此代码移至onCreateViewHolder中?
您在视图持有者内部具有的此块:
priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            checklistModel.setSelectedId(checkedId);
            Log.d(TAG, "onCheckedChanged: " + checkedId);
        }
    });

@Alan,你可以尝试记录生成的“id”和“selectedId”的值,也许你的算法正在生成错误的值,如果是这样,你可以尝试使用“holder.adapterPosition”。 - MadScientist
生成的id和selectedId的值与预期相符。 - Alan Godoi

1
尝试将您的setOnCheckedChangeListener代码移动到ViewHolder并在此处更新mCheckList
priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
        mCheckList.get(getAdapterPosition()).setSelectedId(checkedId);
        Log.d(TAG, "onCheckedChanged: " + radioGroup.getCheckedRadioButtonId() + " " + i);
}});

主要问题在于您没有更新正确的项目状态。当您点击单选按钮时,它只会更新最后一个调用onBindViewHolder的项目,因为checklistModel仅保留了最后一个引用。要解决此问题,您始终需要在侦听器中访问mainList。

1
我已经做了,但我仍然失去无线电检查。 - Alan Godoi
setSelectedId是什么? - IgorGanapolsky

0
在 onClick 事件发生时,将单选按钮的选中/未选中状态保存到您的模型中(即,列表中的项目应该有一个字段用于此)。当您绑定 ViewHolder 时,请确保将复选框的值设置为您在模型中保存的任何内容。

0

独特的工作解决方案是在ViewHolder中将RecyclerView设置为不可回收。

this.setIsRecyclable(false);

这会违背 RecyclerView 的设计初衷。 - MadScientist
我知道它的作用。如果这不是一个选项,那么它就不应该存在。你可以提供有用的答案。 - Alan Godoi
这更像是一种变通方法而不是解决方案。您应该在单选按钮的onCheck回调中更新您的checkList并调用适配器的notifyDataSetChanged方法。 - Vygintas B
我之前做过这个,但是它不起作用。我有其他的RecyclerView按预期工作,但是这一个由于数据类型的原因无法正常工作。 - Alan Godoi
@Alan 你可以尝试调试你的适配器并检查你的列表是否已更新。这值得关注,因为在onBindViewHolder内部的监听器总是有缺陷的。 - Vygintas B
这与RadioGroup有什么关系? - IgorGanapolsky

-1
以下代码怎么样?
适配器:
由于视图是可回收的,我认为唯一标识是一个错误。
int id = (position+1)*100;

int id = 1;

没有区别,发生的事情是一样的。 - Alan Godoi
如果您注释掉上面的代码,会对代码产生什么影响?适配器: //holder.priceGroup.removeAllViews();然而,ID是资源ID。由于它可能与单选按钮以外的其他资源ID重叠,因此最好也在XML中定义单选按钮。 - ysys
在XML中定义单选按钮是不可能的,因为它取决于问题的数量,如果我从服务器获取到3个问题,则需要有3个RadioGroup,并且每个RadioGroup内部有3个RadioButton。如果问题的数量发生变化,则RadioGroup的数量也会发生变化。通过删除“removeAllViews()”,它会像每个问题6个单选按钮一样不断创建单选按钮。 - Alan Godoi
我理解单选按钮的数量需要是动态的。 如果将下面的代码放在for语句之外,会得到什么结果?适配器: holder.priceGroup.check(checklistModel.getSelectedId()); - ysys
已经超出了for循环,我将更新问题并尝试添加一个gif以帮助可视化。 - Alan Godoi

-1

Recyclerview的工作方式是,当您向下或向上滚动直到视图不可见时,它将存储状态,但当您向下滚动recyclerview或向上滚动recyclerview时,它将销毁该行并将一个新行放入该位置。因此,整个行的视图将被销毁。

解决问题的方法是在您正在使用的列表中添加另一个变量,当单选按钮更改状态时,您存储数据并继续进行。 像这样在您的数据模型类中 并且在Model Class中定义recyclerview使用哪个列表。

Boolean is stateclicked;
int state position;

在OnBindViewholder中,您可以获取此数据的值。如果该值为null,则表示未单击,如果单击了,则将布尔值更改为“yes”,并将状态值放入整数中。


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