androidlistviewsimplecursoradapterandroid-viewbinder

how to bind a checkbox to a listview


I have a listview containing on each row a textview with a checkbox, so when the checkbox is checked and we scroll down through the listview the checkbox instance will be taken from a place to another (reused..) and I have several checked checkboxes how to fix that I tried to bind the checkbox to the listview but that didn't work my code is:

 SimpleCursorAdapter adapter =new SimpleCursorAdapter(this,R.layout.rating,cu,new String[]{"Title","Favorites"}, new int[]{R.id.text1,R.id.bt_rating},CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        listv.setAdapter(adapter);

        adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder(){
               /** Binds the Cursor column defined by the specified index to the specified view */
               public boolean setViewValue(View view, Cursor cursor, int columnIndex){
                   if(view.getId() == R.id.bt_rating){

                      ((CheckBox)view).setChecked(Boolean.valueOf(cursor.getString(cursor.getColumnIndex("Favorites"))));
                      ((CheckBox)view).setOnCheckedChangeListener(myCheckChangList);
                       return true; //true because the data was bound to the view
                   }
                   return false;
               }
            });


 OnCheckedChangeListener myCheckChangList = new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView,
                    boolean isChecked) {
                 buttonView.setChecked(isChecked);
            }
        };

My xml code of the content of the row of my listview is:

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

  <CheckBox
 android:id="@+id/bt_rating"
 android:focusable="false"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center_vertical"
 android:button="@android:drawable/btn_star"/>

<TextView

android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/fsinlistview"

 />
</LinearLayout>

Solution

  • It looks like your OnCheckedChangedListener is the problem here. If you look at your code, see that every checkbox is getting a reference to the same listener. So when you check one box, you're setting every other box as checked too - and you're not updating your backing data, either.

    Your OnCheckedChangedListener should not be updating the view state of the checkbox - the callback is fired because the state has already changed.

    So you need to do the following steps when a user checks the checkbox:

    1. Figure out which item was checked, and how that corresponds to your data
    2. Update your data to suit the new checked/unchecked state
    3. Notify your adapter of a data change/update your cursor

    You could do this something like the following, tagging the view with the ID of the row it represents:

    public boolean setViewValue(View view, Cursor cursor, int columnIndex){
        if(view.getId() == R.id.bt_rating){
            view.setTag(cursor.getInt(cursor.getColumnIndex(SomeDBContract.ID)));
            ((CheckBox)view).setChecked(Boolean.valueOf(cursor.getString(cursor.getColumnIndex("Favorites"))));
            ((CheckBox)view).setOnCheckedChangeListener(myCheckChangList);
            return true; //true because the data was bound to the view
        }
        return false;
    }
    

    Then, in your listener you can update your database according to that ID:

    CheckedChangeListener myCheckChangList = new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView,
                    boolean isChecked) {
                 int rowId = (int) buttonView.getTag();
                 // Handle updating the database as per normal
                 updateSomeDbRowAsChecked(rowId, isChecked);
            }
        };
    

    Finally, you'll need to update your cursor adapter with a new cursor once the database row is updated:

     myAdapter.swapCursor(newCursor);
    

    You'll have to adjust all of this to suit your code, but it should give you an idea of one way you can approach this problem.