Search code examples
javaandroidandroid-recyclerviewradio-buttonradio-group

Radio button checked by itself when scrolling


I made a RecyclerView that shows 32 question and 4 answers as RadioButtons. When I click a single radio button, it works, and I show a toast when it is checked.

But when I scroll the RecyclerView`, it shows toasts like I checked a radio button.

How do I fix it?

My adapter:

public class sikapadapter extends RecyclerView.Adapter<sikapadapter.ViewHolder> {

    private int[] integer;
    String option[];
    private OnRadioChangeListener onRadioChangeListener;

    public sikapadapter(String[] option, OnRadioChangeListener onRadioChangeListener) {
        this.option = option;
        this.onRadioChangeListener = onRadioChangeListener;
        integer = new int[option.length];
    }

    interface OnRadioChangeListener{
        void onRadioChange(int position,int checkedId);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.recycleview_sikap_layout,parent,false);
        sikapadapter.ViewHolder aadvh = new sikapadapter.ViewHolder(view);
        return aadvh;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        if(integer[position] == 1){
            holder.radio.check(R.id.radiobutton1);
        }else if(integer[position] == 2){
            holder.radio.check(R.id.radiobutton2);
        }else if(integer[position] == 3){
            holder.radio.check(R.id.radiobutton3);
        }else if(integer[position] == 4){
            holder.radio.check(R.id.radiobutton4);
        }else {
            holder.radio.clearCheck();
        }

        holder.text.setText(option[position]);

        holder.radio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if(onRadioChangeListener!=null){
                    if(checkedId == R.id.radiobutton1){
                        checkedId = 1;
                        integer[position] = 1;
                        onRadioChangeListener.onRadioChange(position,checkedId);
                    }

                    if(checkedId == R.id.radiobutton2){
                        checkedId = 2;
                        integer[position] = 2;
                        onRadioChangeListener.onRadioChange(position,checkedId);
                    }

                    if(checkedId == R.id.radiobutton3){
                        checkedId = 3;
                        integer[position] = 3;
                        onRadioChangeListener.onRadioChange(position,checkedId);
                    }

                    if(checkedId == R.id.radiobutton4){
                        checkedId = 4;
                        integer[position] = 4;
                        onRadioChangeListener.onRadioChange(position,checkedId);

                    }

                }
            }
        });


    }

    @Override
    public int getItemCount() {
        return option.length;
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        TextView text;
        RadioGroup radio;
        RadioButton radio1,radio2,radio3,radio4;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.recytexttampil);
            radio = itemView.findViewById(R.id.radiogroup);
            radio1 = itemView.findViewById(R.id.radiobutton1);
            radio2 = itemView.findViewById(R.id.radiobutton2);
            radio3 = itemView.findViewById(R.id.radiobutton3);
            radio4 = itemView.findViewById(R.id.radiobutton4);


        }
    }
}

Like this - I never checked adapter position 19, but when I scroll it checks itself:

enter image description here


Solution

  • Calling radio.check triggers any OnCheckedChangeListeners, so when onBindViewHolder is called for a recycled ViewHolder, the listener that was attached from the previous bind is triggered.

    To solve it, remove the listener, set the correct checked item, then set the new listener.

    For example:

    // Clear the listener so we don't trigger it
    holder.radio.setOnCheckedChangeListener(null);
    
    // Set the correct item to checked
    if(integer[position] == 1){
        holder.radio.check(R.id.radiobutton1);
    } else if (...) {
        // ...handle other cases here, removed to keep the example short...
    }
    
    holder.text.setText(option[position]);
    
    // Put the listener back to watch for new changes
    holder.radio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        //...