Radiogroup in recyclerview
Asked Answered
C

3

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

        }
    }

}
Charitacharitable answered 2/11, 2015 at 11:50 Comment(1)
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 thoughUndersell
C
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());

Clance answered 2/11, 2015 at 13:4 Comment(2)
Thank you for your response. It still doesnt seem to work :/Charitacharitable
I made the suggested change and removed setChecked(false) and it seemed to work. Thanks so much :)Charitacharitable
W
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();

}

}
Widmer answered 18/7, 2018 at 17:59 Comment(0)
H
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];
                    }
Hopefully answered 6/6, 2017 at 6:32 Comment(1)
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 arrayHopefully

© 2022 - 2024 — McMap. All rights reserved.