viRUS
viRUS

Reputation: 35

RadioGroup doesnt keep properly state in ListView

I know about ListView recycling issue and try to keep my state in model, but it seems not to work. I've read many topics with the same problem, but their solutions dont work for me, or I do something wrong. When I scroll up and down the list view with radio group in initial state, nothing happens, that's right. But as soon as I set checked any radio button in any row and then scroll it up and down radio buttons become checked randomly.

RadioAdapter.java:

public class RadioAdapter extends ArrayAdapter<Question> {

List<Question> mSource;

static class ViewHolder {
    TextView category = null;
    TextView question = null;
    RadioGroup rbGroup = null;

    ViewHolder(View row) {
        this.category = (TextView) row.findViewById(R.id.tvQuestionCategory);
        this.question = (TextView) row.findViewById(R.id.tvQuestion);
        this.rbGroup = (RadioGroup) row.findViewById(R.id.rgIteration);
    }
}

private LayoutInflater mInflater;

public RadioAdapter(Context context, List<Question> mSource) {
    super(context, R.layout.item_question, mSource);
    mInflater = LayoutInflater.from(context);
    this.mSource = mSource;
}

public View getView(final int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    View row = convertView;

    if (row == null) {
        row = mInflater.inflate(R.layout.item_question, null);

        holder = new ViewHolder(row);

        holder.rbGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

            public void onCheckedChanged(RadioGroup group, int checkedId) {
                //Get position of current group
                Integer pos = (Integer) group.getTag();
                switch (checkedId) {
                    case R.id.rbIterationYes:
                        //Check 'YES' and uncheck 'NO'
                        mSource.get(pos).setYesChecked(true);
                        mSource.get(pos).setNoChecked(false);
                        break;

                    case R.id.rbIterationNo:
                        //Vice versa
                        mSource.get(pos).setNoChecked(true);
                        mSource.get(pos).setYesChecked(false);
                        break;
                }
            }
        });

        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }
    holder.rbGroup.setTag(new Integer(position));
    holder.category.setText(mSource.get(position).getCategory());
    holder.question.setText(mSource.get(position).getDescription());
    //If no one of buttons isn't checked
    if (!mSource.get(position).isYesChecked() && !mSource.get(position).isNoChecked()) {
        holder.rbGroup.clearCheck();
    }else {
        //We are supposing 'YES' is checked
        int c = 0;
        //Or 'NO' is checked
        if (mSource.get(position).isNoChecked()){
            c = 1;
        }
        //Set checked button
        ((RadioButton)holder.rbGroup.getChildAt(c)).setChecked(true);
    }
    return row;
}

}

Question.java:

public class Question {

     private Integer id;
     private String category;
     private String description;
     private boolean yesChecked;
     private boolean noChecked;

/* Getters and setters */
}

item_question.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="18dp"
    android:id="@+id/tvQuestionCategory"/>
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="18dp"
    android:id="@+id/tvQuestion"/>

<RadioGroup
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/rgIteration">
        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="false"
            android:id="@+id/rbIterationYes"
            android:text="@string/rbIterationYes"
            android:textSize="16dp"/>
        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="false"
            android:id="@+id/rbIterationNo"
            android:text="@string/rbIterationNo"
            android:textSize="16dp" />
</RadioGroup>

</LinearLayout>

I hope you can help me find mistakes, because I am completely tired seeking solution. Thanks

Upvotes: 2

Views: 435

Answers (2)

Debanjan
Debanjan

Reputation: 2836

Before you are setting checked or unchecked to your RadioGroup, you should disable the listener, like this. Hope this helps. Change your adapter to this

public class RadioAdapter extends ArrayAdapter<Question> {

    List<Question> mSource;
    RadioGroup.OnCheckedChangeListener listener;
    static class ViewHolder {
        TextView category = null;
        TextView question = null;
        RadioGroup rbGroup = null;

        ViewHolder(View row) {
            this.category = (TextView) row.findViewById(R.id.tvQuestionCategory);
            this.question = (TextView) row.findViewById(R.id.tvQuestion);
            this.rbGroup = (RadioGroup) row.findViewById(R.id.rgIteration);
        }
    }

    private LayoutInflater mInflater;

    public RadioAdapter(Context context, List<Question> mSource) {
        super(context, R.layout.item_question, mSource);
        mInflater = LayoutInflater.from(context);
        this.mSource = mSource;
    }

    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        View row = convertView;

        if (row == null) {
            row = mInflater.inflate(R.layout.item_question, null);

            holder = new ViewHolder(row);
            listener = new RadioGroup.OnCheckedChangeListener() {

                public void onCheckedChanged(RadioGroup group, int checkedId) {

                    //Get position of current group
                    Integer pos = (Integer) group.getTag();
                    switch (checkedId) {
                        case R.id.rbIterationYes:
                            //Check 'YES' and uncheck 'NO'
                            mSource.get(pos).setYesChecked(true);
                            mSource.get(pos).setNoChecked(false);
                            break;

                        case R.id.rbIterationNo:
                            //Vice versa
                            mSource.get(pos).setNoChecked(true);
                            mSource.get(pos).setYesChecked(false);
                            break;
                    }
                }
            };
            holder.rbGroup.setOnCheckedChangeListener(listener);

            row.setTag(holder);
        } else {
            holder = (ViewHolder) row.getTag();
        }
        holder.rbGroup.setTag(new Integer(position));
        holder.category.setText(mSource.get(position).getCategory());
        holder.question.setText(mSource.get(position).getDescription());
        //If no one of buttons isn't checked
        holder.rbGroup.setOnCheckedChangeListener(null);
        if (!mSource.get(position).isYesChecked() && !mSource.get(position).isNoChecked()) {
            holder.rbGroup.clearCheck();
        }else {
            //We are supposing 'YES' is checked
            int c = 0;
            //Or 'NO' is checked
            if (mSource.get(position).isNoChecked()){
                c = 1;
            }
            //Set checked button

            ((RadioButton)holder.rbGroup.getChildAt(c)).setChecked(true);

        }
        holder.rbGroup.setOnCheckedChangeListener(listener);
        return row;
    }

}

Upvotes: 1

Wasim K. Memon
Wasim K. Memon

Reputation: 6067

You can do like this override getItemId method and return your question id so you will able to identify each question and answer with id.

public class RadioAdapter extends ArrayAdapter<Question> {

    List<Question> mSource;

    static class ViewHolder {
        TextView category = null;
        TextView question = null;
        RadioGroup rbGroup = null;

        ViewHolder(View row) {
            this.category = (TextView) row.findViewById(R.id.tvQuestionCategory);
            this.question = (TextView) row.findViewById(R.id.tvQuestion);
            this.rbGroup = (RadioGroup) row.findViewById(R.id.rgIteration);
        }
    }

    private LayoutInflater mInflater;

    public RadioAdapter(Context context, List<Question> mSource) {
        super(context, R.layout.item_question, mSource);
        mInflater = LayoutInflater.from(context);
        this.mSource = mSource;
    }

    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        View row = convertView;

        if (row == null) {
            row = mInflater.inflate(R.layout.item_question, null);

            holder = new ViewHolder(row);

            holder.rbGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

                public void onCheckedChanged(RadioGroup group, int checkedId) {
                    //Get position of current group
                    //Integer pos = (Integer) group.getTag();
                    switch (checkedId) {
                        case R.id.rbIterationYes:
                            //Check 'YES' and uncheck 'NO'
                            mSource.get((int) getItemId(position)).setYesChecked(true);
                            mSource.get((int) getItemId(position)).setNoChecked(false);
                            break;

                        case R.id.rbIterationNo:
                            //Vice versa
                            mSource.get((int) getItemId(position)).setNoChecked(true);
                            mSource.get((int) getItemId(position)).setYesChecked(false);
                            break;
                    }
                }
            });

            row.setTag(holder);
        } else {
            holder = (ViewHolder) row.getTag();
        }
        holder.rbGroup.setTag(new Integer(position));
        holder.category.setText(mSource.get(position).getCategory());
        holder.question.setText(mSource.get(position).getDescription());
        //If no one of buttons isn't checked
        if (!mSource.get((int) getItemId(position)).isYesChecked() && !mSource.get((int) getItemId(position)).isNoChecked()) {
            holder.rbGroup.clearCheck();
        } else {
            //We are supposing 'YES' is checked
            int c = 0;
            //Or 'NO' is checked
            if (mSource.get((int) getItemId(position)).isNoChecked()) {
                c = 1;
            }
            //Set checked button
            ((RadioButton) holder.rbGroup.getChildAt(c)).setChecked(true);
        }
        return row;
    }

    @Override
    public long getItemId(int position) {
        return mSource.get(position).getId();
    }
}

Upvotes: 0

Related Questions