CheckBox gets unchecked on scroll in a custom listview
I know that this question has been asked over and over again but still I've not been a able to find a suggestion that really helps me. The checkbox is unchecked whenever the list is scrolled down. Yes I'm using a boolean array to store the values but this still doesn't fix the problem. Here is my code. Please suggest a solution for this. Thank you.
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
final ViewHolder holder;
final boolean[] itemChecked=new boolean[30];
LayoutInflater inflater = context.getLayoutInflater();
if(convertView==null)
{
convertView = inflater.inflate(R.layout.custom_list, null);
holder = new ViewHolder();
holder.txtViewTitle = (TextView) convertView.findViewById(R.id.title_text);
holder.txtViewDescription = (TextView) convertView.findViewById(R.id.description_text);
holder.cb=(CheckBox) convertView.findViewById(R.id.cb);
convertView.setTag(holder);
}
else
{
holder=(ViewHolder)convertView.getTag();
}
holder.cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO Auto-generated method stub
itemChecked[position] = isChecked;
if(itemChecked[position])
{
holder.cb.setChecked(true);
}
else
{
holder.cb.setChecked(false);
}
holder.txtViewTitle.setText(title[position]);
holder.txtViewDescription.setText(description[position]);
holder.cb.setChecked(itemChecked[position]);
holder.txtViewDescription.setFocusable(false);
holder.txtViewTitle.setFocusable(false);
return convertView;
}
}
getView() is called whenever a previously invisible list item needs to be drawn. Since you recreate itemChecked[]
each time this method is called you will have the new checkbox unchecked and a different Array for each resulting View. (final in Java does not make that field unique like in C)
Simplest way to solve that is to make itemChecked
a classmember and set / restore checkbox state based on that one.
public class MyListAdapter extends ArrayAdapter<Object> {
private final boolean[] mCheckedState;
private final Context mContext;
public MyListAdapter(Context context, int resource, int textViewResourceId, List<Object> objects) {
super(context, resource, textViewResourceId, objects);
mCheckedState = new boolean[objects.size()];
mContext = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// simplified to just a Checkbox
// ViewHolder and OnCheckedChangeListener stuff left out
CheckBox result = (CheckBox)convertView;
if (result == null) {
result = new CheckBox(mContext);
}
result.setChecked(mCheckedState[position]);
return result;
}
}
Here is an example. Read the comments in the getView(...) of the adapter provided below.
class TaskObject {
private int pid;
private String processName;
private boolean toKill;
///getters/setters
public boolean isToKill() {
return toKill;
}
public void setToKill(boolean toKill) {
this.toKill = toKill;
}
................................
}
class TaskListAdapter extends BaseAdapter {
private static final String TAG = "adapter";
ArrayList<TaskObject> list;
Context context;
public TaskListAdapter(Context context) {
Log.d(TAG, "created new task list adapter");
this.context = context;
if (list == null) {
list = new ArrayList<TaskObject>();
}
}
public void addTask(TaskObject taskObject) {
list.add(taskObject);
}
public void clearTasks() {
list.clear();
Log.d(TAG, "list size:" + list.size());
this.notifyDataSetChanged();
}
public int getCount() {
return list.size();
}
public TaskObject getItem(int position) {
return list.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
RelativeLayout rl = new RelativeLayout(context);
TextView textPid = new TextView(context);
textPid.setText(Integer.toString(getItem(position).getPid()));
TextView textName = new TextView(context);
textName.setText(getItem(position).getProcessName());
/////Here is your and it will set back your checked item after scroll
CheckBox chckKill = new CheckBox(context);
if(getItem(position).isToKill())
{
chckKill.setChecked(true);
}
////////////////////////////////////////////////////////////////////
chckKill.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//is chkIos checked?
if (((CheckBox) v).isChecked()) {
getItem(position).setToKill(true);
}
}
});
chckKill.setTag(getItem(position).getPid());
/////////NOT LAYOUTED USE LAYOUT
rl.addView(chckKill);
rl.addView(textName);
rl.addView(textPid);
return rl;
}
hope it helps abit.
In mycase, I solved this issue as follows :
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
TextView title = null;
ImageView thumbnail = null;
CheckBox checkBox = null;
Content rowData = GridViewActivity.contents.get(position);
if (null == convertView) {
convertView = mInflater.inflate(R.layout.grid_item, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
title = holder.getContentTitle();
title.setText(rowData.getTitle());
thumbnail = holder.getThumbnail();
thumbnail.setImageResource(rowData.getIcon());
checkBox = holder.getCheckBox();
checkBox.setTag(position);
checkBox.setChecked(rowData.isCheckBox());
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
int getPosition = (Integer) buttonView.getTag();
GridViewActivity.notifyCheckChanges(getPosition,
buttonView.isChecked());
}
});
return convertView;
}