9

I am having a recyclerview where each list item has a radiogroup with 4 radio buttons. How can I store the state of each radiogroup correctly. Here is my code. On scrolling up/down the states are incorrects. Thanks

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

    private List<Element> elements = new ArrayList<>();
    private Context context;

    private int[] state;

    public ElementListAdapter(Context context, List<Element> elements) {
        this.context = context;
        this.elements = elements;

        this.state = new int[elements.size()];
        Arrays.fill(this.state, -1);
    }

    @Override
    public ElementListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_layout, parent,
                false);

        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        final Element ele = elements.get(position);
        final String title = ele.getTitle();
        final String description = ele.getDescription();

        // Set text
        holder.tvTitle.setText(title);
        holder.tvDesciption.setText(description);

        if (ele.isHeader()) {                
            holder.radioGroup.setVisibility(View.GONE);
        } else {               
            holder.radioGroup.setVisibility(View.VISIBLE);
        }

        setRadio(holder, this.state[position]);



        holder.rb1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                state[position] = 0;
                setRadio(holder, state[position]);
            }
        });
        holder.rb2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                state[position] = 1;
                setRadio(holder, state[position]);
            }
        });
        holder.rb3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                state[position] = 2;
                setRadio(holder, state[position]);
            }
        });
        holder.rb4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                state[position] = 3;
                setRadio(holder, state[position]);
            }
        });


    }

    private void setRadio(final ViewHolder holder, int selection) {

        System.out.println("SELECT:" + selection);
        RadioButton b1 = holder.rb1;
        RadioButton b2 = holder.rb2;
        RadioButton b3 = holder.rb3;
        RadioButton b4 = holder.rb4;

        b1.setChecked(false);
        b2.setChecked(false);
        b3.setChecked(false);
        b4.setChecked(false);

        if (selection == 0) b1.setChecked(true);
        if (selection == 1) b2.setChecked(true);
        if (selection == 2) b3.setChecked(true);
        if (selection == 3) b4.setChecked(true);
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {

        public View view;
        public TextView tvTitle;
        public TextView tvDesciption;

        public RadioGroup radioGroup;
        public RadioButton rb1, rb2, rb3, rb4;


        public ViewHolder(View itemView) {
            super(itemView);

            view = itemView;
            tvTitle = (TextView) itemView.findViewById(R.id.title);
            tvDesciption = (TextView) itemView.findViewById(R.id.description);

            radioGroup = (RadioGroup) itemView.findViewById(R.id.radioGroup);
            rb1 = (RadioButton) itemView.findViewById(R.id.rb1);
            rb2 = (RadioButton) itemView.findViewById(R.id.rb2);
            rb3 = (RadioButton) itemView.findViewById(R.id.rb3);
            rb4 = (RadioButton) itemView.findViewById(R.id.rb4);

        }
    }

}
sujithvm
  • 2,351
  • 3
  • 15
  • 16
  • The view holders recycle, so you could not hold the state by default. One solution is to dynamically store the state (check adapter positions) and restore them in onBindViewHolder. Don't trigger any onCheckChange though – Nguyễn Hoài Nam Nov 02 '15 at 12:03

3 Answers3

11

The best way saving your item state is placing the state variable inside the item model of the list, ex : "Element" in your case, than inside onBindViewHolder set the state based on your model, in your case:

change this :setRadio(holder, this.state[position]);

to this : setRadio(holder, elements.get(position).getState());

and

inside onClick methods

ex: for the first one change this: state[position] = 0; setRadio(holder, this.state[position]);

to this : elements.get(position).setState(0); setRadio(holder, elements.get(position).getState());

KRist
  • 1,402
  • 12
  • 10
1

The best way and the most efficient to do this is to simply add a variable which holds the option(radio button selected) to the class. And after onClick() just assign which radio button is selected to the variable and fetch that.

First, add a variable in your Element class which stores the radio button option selected.

Then,

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

private List<Element> elements = new ArrayList<>();
private Context context;

//instance of interface created
final private ListItemClickListener myOnClickListener;

private int[] state;

//create a interface which helps to communicates with your main activity
public interface ListItemClickListener
{
    void onListItemClick(int clickItemIndex,String optionSelected);
}
public ElementListAdapter(Context context, List<Element> elements) {
    this.context = context;
    this.elements = elements;

    this.state = new int[elements.size()];
    Arrays.fill(this.state, -1);
}

@Override
public ElementListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_layout, parent,
            false);

    return new ViewHolder(v);
}

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    final Element ele = elements.get(position);
    final String title = ele.getTitle();
    final String description = ele.getDescription();

    // Set text
    holder.tvTitle.setText(title);
    holder.tvDesciption.setText(description);

    if (ele.isHeader()) {                
        holder.radioGroup.setVisibility(View.GONE);
    } else {               
        holder.radioGroup.setVisibility(View.VISIBLE);
    }

  holder.setIsRecyclable(false);

  //here you check the option selected
    switch (Element.getUserOption()) {
           case "1":
                   holder.rb1.setChecked(true);
               break;
           case "2":
                   holder.rb2.setChecked(true);
               break;
           case "3":
                   holder.rb3.setChecked(true);
               break;
           case "4":
                   holder.rb4.setChecked(true);
               break;
           default:
               holder.radioGroup.clearCheck();
      }
}

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

//make sure to implement onClickListener here
public static class ViewHolder extends RecyclerView.ViewHolder implements View.onClickListener {

    public View view;
    public TextView tvTitle;
    public TextView tvDesciption;
    public int clickedCardPosition;

    public RadioGroup radioGroup;
    public RadioButton rb1, rb2, rb3, rb4;


    public ViewHolder(View itemView) {
        super(itemView);

        view = itemView;
        tvTitle = (TextView) itemView.findViewById(R.id.title);
        tvDesciption = (TextView) itemView.findViewById(R.id.description);

        radioGroup = (RadioGroup) itemView.findViewById(R.id.radioGroup);
        rb1 = (RadioButton) itemView.findViewById(R.id.rb1);
        rb2 = (RadioButton) itemView.findViewById(R.id.rb2);
        rb3 = (RadioButton) itemView.findViewById(R.id.rb3);
        rb4 = (RadioButton) itemView.findViewById(R.id.rb4);

        rb1.setOnClickListener(this);
        rb2.setOnClickListener(this);
        rb3.setOnClickListener(this);
        rb4.setOnClickListener(this);

    }


@Override
    public void onClick(View view) {

        int clickedCardPosition = getAdapterPosition();

        if(rb1.isPressed()) myOnClickListener.onListItemClick(clickedCardPosition, "1");
        if(rb2.isPressed()) myOnClickListener.onListItemClick(clickedCardPosition,"2");
        if(rb3.isPressed()) myOnClickListener.onListItemClick(clickedCardPosition,"3");
        if(rb4.isPressed()) myOnClickListener.onListItemClick(clickedCardPosition,"4");

    }

}
}

Then, in your activity where you are assigning the adapter, implement the Interface.

//implement here
public class YourMainActivity extends AppCompatActivity implements 
recyclerAdapter.ListItemClickListener{
.
.
.
.
.
@Override
public void onListItemClick(int clickItemIndex, String optionSelected) {

       //Here assign the value to the Element Obj
        yourList.get(clickItemIndex).setUserOption(optionSelected);
        yourAdapter.notifyDataSetChanged();

}

}
sac
  • 63
  • 1
  • 8
0

this is what I did. In my onBindViewHolder

if(visualScore[position] == 1 ){
                holder.rg.check(R.id.rb_1);
            }else{
                if(audioScore[position] == 1){
                    holder.rg.check(R.id.rb_2);
                }else{
                    if(kinestScore[position] == 1){
                        holder.rg.check(R.id.rb_3);
                    }else{
                        holder.rg.clearCheck();
                    }
                }
            }
            holder.rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
                    switch (checkedId) {
                        case (R.id.rb_1):
                            visualScore[position] = 1;
                            audioScore[position] = 0;
                            kinestScore[position] = 0;
                            break;
                        case (R.id.rb_2):
                            visualScore[position] = 0;
                            audioScore[position] = 1;
                            kinestScore[position] = 0;
                            break;
                        case (R.id.rb_3):
                            visualScore[position] = 0;
                            audioScore[position] = 0;
                            kinestScore[position] = 1;
                            break;
                    }
        });

I declared all the score arrays as private global methods in the adapter class. Then, when submit button is clicked :

int v = 0, a = 0, k = 0;
                    for (int i = 0; i < Questions.length; i++){
                        v += visualScore[i];
                        a += audioScore[i];
                        k += kinestScore[i];
                    }
Asyraf Arifin
  • 2,445
  • 2
  • 7
  • 11
  • not saying that my method is better, but it's a workaround. onCheckedChanged will be triggered when the recyclerview is scolled, but the value is still as we saved in the array – Asyraf Arifin Jun 06 '17 at 06:33