Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checked RadioButton Recycling

I'm adding radiogroups programatically to my recyclerview and it's working fine. But when I check it and scroll the recyclerview it loses checked radios.

I've seen many ways and examples of solutions, but I cannot achieve it. It's been some days in a row.

I'm saving the checked radio in model as you can see in code below.

Adapter:

@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);
        }
    });

}

Holder

    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);
    }
}

EDIT 1 - Radio Group

<?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>

EDIT 2

As requested, here is the important code from my 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();
}

RecyclerView with RadioButtons

EDIT 3 - Important

The RadioGroups and RadioButtons are programatically generated because I'm getting all questions from server, the number of questions are different depending on previous selections made by user, that's why I need it this way.

EDIT 4

GIF to enhance the problem visualization

EDIT 5 - Checklist Model Class

    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;
    }
}
like image 769
Alan Godoi Avatar asked Sep 09 '26 11:09

Alan Godoi


2 Answers

Though i'm not sure as to whether this will solve your problem or not, but as an optimiziation also a good practice you should attach listeners to the onCreateViewHolder instead of the onBindViewHolder this prevents multiple objects from getting created for the listener.

Why dont you move this code inside the onCreateViewHolder

this block inside the view holder you have:

priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            checklistModel.setSelectedId(checkedId);
            Log.d(TAG, "onCheckedChanged: " + checkedId);
        }
    });
like image 138
MadScientist Avatar answered Sep 16 '26 01:09

MadScientist


Try moving your setOnCheckedChangeListener code to ViewHolder and update your mCheckList here:


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);
}});

Main problem is that you're not updating correct items state. When you click on radio button it will update only last item onBindViewHolder called because checklistModel holds only last reference. To fix this you always need to access mainList inside listeners.

like image 25
Vygintas B Avatar answered Sep 16 '26 01:09

Vygintas B



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!