Android: CursorAdapter, ListView and CheckBox
Asked Answered
G

8

16

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.

Gamages answered 26/1, 2011 at 11:0 Comment(1)
I'm very sorry... didn't know about that... Reading faq)Gamages
M
4

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.

Mcdonough answered 26/1, 2011 at 15:39 Comment(1)
Oh, God! it helped! Thanks a lot! I downloaded google alarm from GIT repository and they used similar approach! It's melted my brain a bit)Gamages
S
65

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 DataAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private ArrayList<String> list = new ArrayList<String>();
private ArrayList<Boolean> checkedItems = new ArrayList<Boolean>();

public DataAdapter(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++) {
        checkedItems.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.list_item, null);
    }

    final CheckBox checkBox = (CheckBox) inView.findViewById(R.id.bcheck);
    checkBox.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            CheckBox cb = (CheckBox) v;
            if (cb.isChecked()) {
                checkedItems.set(pos, true);
            } else if (!cb.isChecked()) {
                checkedItems.set(pos, false);
            }
        }
    });
    checkBox.setChecked(checkedItems.get(pos));
    return inView;
}}
Swihart answered 26/1, 2011 at 12:4 Comment(6)
I'll try right now... the only thing is I have bindView and newView, but I think it's the sameGamages
+1. I have been searching for an answer for hours. Clean and simple.Cacography
This totally worked for me, even when I had a custom adapter with section headers. Very simple and cool.Duyne
Hi Vikas! im currently using something very similar to your post using an arrayadapter instead, but im having a problem in getting all the checked elements if they're not visible in the list from activity(not listactivity)Adduction
Very neat without being complex. Thanks for this approach @Vikas Patidar!Tesch
I don't understand why is necessary to define cb instead of using cbox directly?Jeanene
M
4

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.

Mcdonough answered 26/1, 2011 at 15:39 Comment(1)
Oh, God! it helped! Thanks a lot! I downloaded google alarm from GIT repository and they used similar approach! It's melted my brain a bit)Gamages
E
4

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.

Expressage answered 1/2, 2012 at 7:10 Comment(2)
Hi Ayush Pateria, what is the object "selected" in your code above?Gristmill
Well, selected is a list of all the items which are checked.Expressage
H
2

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();
}
Hemo answered 14/10, 2012 at 15:5 Comment(0)
O
0

I can't understand, why it is not possible (like in HTML) to define a ViewElement as an array in XML. I.e.:

<CheckBox
    android:id="@+id/myCheckBox[]"
    android:focusable="false"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true" />

and store a value int it [true|false]. This would make life much easier, instead of using complicated source-frickle. Also the data (i.e. when switching from landscape to portrait) should be kept by android and not getting lost.

Perhaps in API 117, version toothscratch...

Odont answered 25/10, 2013 at 10:4 Comment(0)
H
0

(sry, cannot comment yet) - regarding Answer of Vikas Patidar, which works for me - be aware when using async loading (like using a Loader) because getCount in the Constructor of the Adapter will be zero.

You end up with indexing errors (cause the ArrayList never gets populated). Use SparseBooleanArray instead of ArrayList and it will work with async loading as well.

Halfbound answered 21/9, 2014 at 11:43 Comment(0)
B
0

Might be late here, to add up on Vikas Patidar's answer here is a complete implementation using the bindView method for those of you who are suing a CursorAdapter:

@Override
        public void bindView(View view, Context context, Cursor cursor) {

            final ViewHolder holder = (ViewHolder) view.getTag();
            final int position = cursor.getPosition();
            String name = (cursor.getString(cursor.getColumnIndexOrThrow("NotificationDateFor")));
            String image = cursor.getString(cursor.getColumnIndexOrThrow("imageUri"));

             System.out.println("cursor.getPosition(test): "+cursor.getPosition());

            holder.nametext.setText(name);
        //  setImage(image, holder.iv); 


            holder.chk.setOnClickListener(new OnClickListener(){

                @Override
                public void onClick(View v) {

                        if (holder.chk.isChecked()) {
                            itemChecked.set(position, true);
                           System.out.println("cursor.getPosition(true): "+position);
                        } else if (!holder.chk.isChecked()) {
                            itemChecked.set(position, false);
                            System.out.println("cursor.getPosition(false): "+position);
                            // AddDelete
                        }   
                }               
            });

            holder.chk.setChecked(itemChecked.get(cursor.getPosition()));

        }

Note: final int position = cursor.getPosition(); is significant!

Binomial answered 23/3, 2015 at 13:21 Comment(0)
A
0

You can save yourself alot of hassle by utilizing Listview's built-in multiple selection mode.

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

Aldebaran answered 11/5, 2016 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.