Android – Checkboxes in ListView

I have an app which uses a ViewPager which contains Questions and answers below each question.
The questions are two type: single choice or multi choice. One question can have more than 30 answers.

For the single choice we check if we have another option selected and keep the id of the last selected answer. Here is the code:

public class SingleChoiceAnswersAdapter extends ArrayAdapter<QuestionAnswer> {

private int mResourceId = 0;
private final LayoutInflater mLayoutInflater;
private RadioButton mSelectedRB;
private final List<QuestionAnswer> mAnswers;
private int mSelectedPosition = -1;

public SingleChoiceAnswersAdapter(Context context, int resource,
List<QuestionAnswer> objects) {
super(context, resource, objects);
mResourceId = resource;
mLayoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mAnswers = objects;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
QuestionAnswer answer = mAnswers.get(position);

if (view == null) {

view = mLayoutInflater.inflate(mResourceId, parent, false);
holder = new ViewHolder();

holder.radioBtn = (RadioButton) view.findViewById(R.id.rb_answer);
holder.name = (TextView) view.findViewById(R.id.tv_answer_name);

view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}

if (answer.isSelected()) {
mSelectedPosition = position;
mSelectedRB = holder.radioBtn;
}

view.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
ViewHolder holder2 = new ViewHolder();
holder2.radioBtn = (RadioButton) v.findViewById(R.id.rb_answer);
if (!holder2.radioBtn.isChecked()) {
holder2.radioBtn.setChecked(true);
}

if ((position != mSelectedPosition && mSelectedRB != null)) {
mSelectedRB.setChecked(false);
getItem(mSelectedPosition).setSelected(false);
}
getItem(position).setSelected(true);
mSelectedPosition = position;
mSelectedRB = holder2.radioBtn;

}
});

holder.radioBtn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if ((position != mSelectedPosition && mSelectedRB != null)) {
mSelectedRB.setChecked(false);
getItem(mSelectedPosition).setSelected(false);
}
getItem(position).setSelected(true);
mSelectedPosition = position;
mSelectedRB = (RadioButton) v;

}
});

if (mSelectedPosition != position) {
holder.radioBtn.setChecked(false);
} else {
holder.radioBtn.setChecked(true);
if (mSelectedRB != null && holder.radioBtn != mSelectedRB) {
mSelectedRB = holder.radioBtn;
}
}

holder.name.setText(getItem(position).getDisplayName() + "");

return view;
}

private class ViewHolder {
TextView name;
RadioButton radioBtn;
}
}

But for the multichoice adapter the things are a little bit different. Android has a nasty bug when you have more than 20 checkboxes and when you scroll up or down on the screen it selects a random answer because it reuses the checkbox. That will result in a strange behaviour when you scroll up and down which will select a random checkbox on each scroll.

To handle this case you only need to make sure that when you initialize the CheckBox in the adapter you always make the checkbox selected or not selected. It always must have a state in which it is.This fixes the bug.
Here is the code:

public class MultiChoiceAnswersAdapter extends ArrayAdapter<QuestionAnswer> {

private int mResourceId = 0;
private LayoutInflater mLayoutInflater;
private List<QuestionAnswer> mAnswers;
private int mPosition = 0;

public MultiChoiceAnswersAdapter(Context context, int resource,
List<QuestionAnswer> objects) {
super(context, resource, objects);
mResourceId = resource;
mAnswers = objects;
}

@SuppressLint("NewApi")
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
mPosition = position;

QuestionAnswer answer = mAnswers.get(position);

if (convertView == null) {
mLayoutInflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

convertView = mLayoutInflater.inflate(mResourceId, parent, false);
holder = new ViewHolder();

holder.name = (TextView) convertView.findViewById(R.id.tv_mc_answer_name);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.cb_answer);
holder.checkBox.setTag(position);

convertView.setTag(holder);

} else {
holder = (ViewHolder) convertView.getTag();
}

holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(false);

if (answer.isSelected()) {
holder.checkBox.setChecked(true);
}

convertView.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
boolean isChecked = false;
ViewHolder holder2 = new ViewHolder();
holder2.checkBox = (CheckBox) v.findViewById(R.id.cb_answer);
if (holder2.checkBox.isChecked()) {
isChecked = false;
holder2.checkBox.setChecked(false);
} else {
isChecked = true;
holder2.checkBox.setChecked(true);
}

getItem(position).setSelected(isChecked);

}
});

holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mAnswers.get(position).setSelected(isChecked);
notifyDataSetChanged();
}
});

holder.name.setText(getItem(position).getDisplayName() + "");

return convertView;
}

public class ViewHolder {
TextView name;
CheckBox checkBox;
}
}

You may also like...