Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: CursorAdapter, ListView and CheckBox

I have ListView with my own layout and CustomCursorAdapter. Every row has it's own checkbox. So... it's absolutely clear that during sroll the checkboxes loose their states. The only stuff I found is Android save Checkbox State in ListView with Cursor Adapter but there is no answer there. And one more question. I had the same problem with my CustorArrayAdapter. I solved that problem using SparseBooleanArray to keep checkboxes states. It works fine, but every scroll calls onCheckedChanged. That's normal? The deal is my list view describes alarm elements and periodic calls (of onCheckedChanged) start/stop the alarms. A lot of unnesseccary actions.

like image 599
Nick Avatar asked Jan 26 '11 11:01

Nick


4 Answers

I had the similar issue with my ListView with CheckBox and what I did to get rid of the problem:

  • Create an ArrayList of Boolean Object to store the state of the each CheckBox
  • Initializes the ArrayList items to default value false, means no CheckBox is checked yet.
  • When you click on CheckBox. Set a check against Checked/Unchecked state and store that value in ArrayList.
  • Now set that position to CheckBox using setChecked() method.

See this code snippet:

public class MyDataAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private ArrayList<String> list = new ArrayList<String>();
private ArrayList<Boolean> itemChecked = new ArrayList<Boolean>();

// itemChecked will store the position of the checked items.

public MyDataAdapter(Context context, int layout, Cursor c, String[] from,
        int[] to) {
    super(context, layout, c, from, to);
    this.c = c;
    this.context = context;

    for (int i = 0; i < this.getCount(); i++) {
        itemChecked.add(i, false); // initializes all items value with false
    }
}

public View getView(final int pos, View inView, ViewGroup parent) {
    if (inView == null) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inView = inflater.inflate(R.layout.your_layout_file, null);
    }

    final CheckBox cBox = (CheckBox) inView.findViewById(R.id.bcheck); // your
    // CheckBox
    cBox.setOnClickListener(new OnClickListener() {

        public void onClick(View v) {

            CheckBox cb = (CheckBox) v.findViewById(R.id.your_checkbox_id);

            if (cb.isChecked()) {
                itemChecked.set(pos, true);
                // do some operations here
            } else if (!cb.isChecked()) {
                itemChecked.set(pos, false);
                // do some operations here
            }
        }
    });
    cBox.setChecked(itemChecked.get(pos)); // this will Check or Uncheck the
    // CheckBox in ListView
    // according to their original
    // position and CheckBox never
    // loss his State when you
    // Scroll the List Items.
    return inView;
}}
like image 156
Vikas Patidar Avatar answered Oct 19 '22 03:10

Vikas Patidar


There are a few concerns with the ListView when having checkable items in it. I would suggest the following link:

http://tokudu.com/2010/android-checkable-linear-layout/

I think it's close to what you want.

like image 26
dbm Avatar answered Oct 19 '22 03:10

dbm


I was also facing a similar kind of problem, so after lot of reading I solved this problem like this:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.listview, null);
            holder = new ViewHolder();
            holder.nameView = (TextView)convertView.findViewById(R.id.textView1);
            holder.numberView = (TextView)convertView.findViewById(R.id.textView2);
            holder.cb = (CheckBox)convertView.findViewById(R.id.checkBox1);
            convertView.setTag(holder);                
        } else {
            holder = (ViewHolder)convertView.getTag();
        }
        holder.nameView.setText(mData.get(position).toString());
        holder.numberView.setText(mNumber.get(position).toString());
        holder.cb.setChecked(false);
        holder.cb.setTag(position);



        if(selected.indexOf(mNumber.get(position).toString()) >= 0)
        {

        holder.cb.setChecked(true);
        }

        return convertView;
    }

}

Here what I am doing that on getView() I am unchecking all the checkboxes and checking again manually those which I need to be checked according to the textview it corresponds. So if the user scroll down after checking the first checkbox, all the checkbox in the view will get unchecked and if he again scrolls up then also all the checkboxes will be unchecked but then the one he clicked before will be again rechecked.

like image 42
Ayush Pateria Avatar answered Oct 19 '22 02:10

Ayush Pateria


I decided to try with setViewBinder method. Сheckboxes state is stored in SparseBooleanArray. Сheckbox state changes as you click checkbox itself, and on the entire cell.

My Row: | TextView | CheckBox |

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
//        ..
    private SparseBooleanArray checkedItems = new SparseBooleanArray(); //for storing item state

    startManagingCursor(cursor);
    String[] from = new String[] { dbAdapter.COLUMN_NAME, dbAdapter.COLUMN_CHECK };
    int[] to = new int[] { R.id.item_name, R.id.item_check };
    recordsAdapter = new SimpleCursorAdapter(this, R.layout.items, cursor, from, to);

    recordsAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
        public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
            if (columnIndex == 2) { //2 - R.id.item_check
                    final CheckBox cb = (CheckBox) view; 
                    final int rowID = cursor.getInt(0); //cursor.getInt(0) - _id from table

                    if (checkedItems.indexOfKey(rowID) >= 0) {  //checkedItems contains rowID?
                        cb.setChecked(checkedItems.get(rowID));
                    } else if (cursor.getInt(2) > 0) {          //cursor.getInt(2): 0 - false, 1 - true
                        checkedItems.append(rowID, true);
                        cb.setChecked(true);
                    } else {
                        cb.setChecked(false);
                    }

                    cb.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (checkedItems.indexOfKey(rowID) >= 0) {
                                checkedItems.put(rowID, !(checkedItems.get(rowID)));
                            } else {
                                checkedItems.append(rowID, true);
                            }
                            cb.setChecked(checkedItems.get(rowID));
                            dbAdapter.updateItem(rowID, checkedItems.get(rowID)?1:0);
                        }
                    });
                    return true;
            }
            return false;
        }
    });

    itemsList.setAdapter(recordsAdapter);
    itemsList.setOnItemClickListener(this);
//      ..
}

//if click on TextView of row - CheckBox of row is set/unset
@Override
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
    TextView tv = (TextView) view.findViewById(R.id.item_name);
    ViewGroup row = (ViewGroup) tv.getParent();
    CheckBox cb = (CheckBox) row.getChildAt(1);
    cb.performClick();
}
like image 30
LionKing Avatar answered Oct 19 '22 04:10

LionKing