Saving checkbox states in a list using CursorAdapter

In my Android project, I have a list that has a checkbox for each item. Data is loaded from the SQLite database using the CursorAdapter class. However, whenever I scroll, the checkbox positions will be moved and moved to the next part of the list. How can I fix this problem? GIF of my problem with CheckBox Here is my cursor adapter class:

public class VocabCursorAdapter extends CursorAdapter { private static final int DIFFICULT = 0; private static final int FAMILIAR = 1; private static final int EASY = 2; private static final int PERFECT = 3; public VocabCursorAdapter(Context context, Cursor c, int flags) { super(context, c, 0); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return LayoutInflater.from(context).inflate(R.layout.item_vocab, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { // Find fields to populate in inflated template TextView tvVocabName = (TextView) view.findViewById(R.id.vocabName); TextView tvVocabDefinition = (TextView) view.findViewById(R.id.vocabDefinition); ImageView tvVocabLevel = (ImageView) view.findViewById(R.id.vocabLevel); // Extract properties from cursor String vocab = cursor.getString(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_VOCAB)); String definition = cursor.getString(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_DEFINITION)); int level = cursor.getInt(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_LEVEL)); // Populate fields with extracted properties tvVocabName.setText(vocab); tvVocabDefinition.setText(definition); if (level == DIFFICULT) { tvVocabLevel.setImageResource(R.drawable.level_bars_difficult); tvVocabLevel.setTag(DIFFICULT); } else if (level == FAMILIAR) { tvVocabLevel.setImageResource(R.drawable.level_bars_familiar); tvVocabLevel.setTag(FAMILIAR); } else if (level == EASY) { tvVocabLevel.setImageResource(R.drawable.level_bars_easy); tvVocabLevel.setTag(EASY); } else if (level == PERFECT) { tvVocabLevel.setImageResource(R.drawable.level_bars_perfect); tvVocabLevel.setTag(PERFECT); } } 

And here is my xml list item, item_vocab.xml:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:longClickable="true"> <ImageView android:layout_width="36sp" android:layout_height="36sp" android:id="@+id/vocabLevel" android:layout_gravity="right" android:src="@drawable/level_bars" android:scaleType="fitXY" android:contentDescription="@string/vocab_level" android:layout_centerVertical="true" android:layout_toLeftOf="@+id/editCheckbox" android:layout_toStartOf="@+id/editCheckbox"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Large Text" android:id="@+id/vocabName" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_toLeftOf="@+id/vocabLevel" android:layout_toStartOf="@+id/vocabLevel"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:text="Small Text" android:id="@+id/vocabDefinition" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_toLeftOf="@+id/vocabLevel" android:layout_toStartOf="@+id/vocabLevel" android:layout_below="@id/vocabName"/> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editCheckbox" android:layout_centerVertical="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true"/> </RelativeLayout> 

And here is my xml that contains listview

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".controller.MyVocab" android:paddingLeft="5dp"> <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/mVocabList" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/empty_text_view" android:id="@android:id/empty" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"/> </RelativeLayout> 

I looked at many different solutions in StackOverflow, but I could not successfully do this in my application. For example, this post has a similar problem, but its solution uses getView, and I could not figure out how to implement it using newView and bindView.

And some other solutions may be examples when the cursor adapter is not involved. Any help is greatly appreciated, thanks a lot! Edit # 1: after enabling Phan changes, the state of the checkbox returns resets to false, and does not save its state when scrolling through the list (see GIF )

+5
android checkbox listview android-cursoradapter android-checkbox
source share
2 answers

Reason: ListView is reusing views.

Decision:

 class VocabCursorAdapter extends CursorAdapter { List<Integer> selectedItemsPositions;//to store all selected items position public VocabCursorAdapter(Context context, Cursor c,int flags) { super(context, c,0); selectedItemsPositions = new ArrayList<>(); } @Override public View newView(Context context, Cursor cursor, ViewGroup viewGroup) { View view = LayoutInflater.from(context).inflate(R.layout.item_vocab, viewGroup, false); CheckBox box = (CheckBox) view.findViewById(R.id.editCheckbox); box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { int position = (int) compoundButton.getTag(); if (b) { //check whether its already selected or not if (!selectedItemsPositions.contains(position)) selectedItemsPositions.add(position); } else { //remove position if unchecked checked item selectedItemsPositions.remove((Object) position); } } }); return view; } @Override public void bindView(View view, Context context, Cursor cursor) { //your other stuff CheckBox box = (CheckBox) view.findViewById(R.id.editCheckbox); box.setTag(cursor.getPosition()); if (selectedItemsPositions.contains(cursor.getPosition())) box.setChecked(true); else box.setChecked(false); } } 

happyCoding;

+3
source share

try it

 public class VocabCursorAdapter extends CursorAdapter { private ArrayList<Boolean> itemChecked = new ArrayList<Boolean>(); // array list for store state of each checkbox public VocabCursorAdapter(Context context, Cursor c, int flags) { for (int i = 0; i < c.getCount(); i++) { // c.getCount() return total number of your Cursor itemChecked.add(i, false); // initializes all items value with false } } @Override public void bindView(View view, Context context, Cursor cursor) { ... final int position = cursor.getPosition(); // get position by cursor CheckBox checkBox = (CheckBox) view.findViewById(R.id.editCheckbox); checkBox.setOnClickListener(new OnClickListener() { public void onClick(View v) { if (itemChecked.get(position) == true) { // if current checkbox is checked, when you click -> change it to false itemChecked.set(position, false); } else { itemChecked.set(position, true); } } }); checkBox.setChecked(itemChecked.get(position)); // set the checkbox state base on arraylist object state Log.i("In VocabCursorAdapter","position: "+position+" - checkbox state: "+itemChecked.get(position)); } } 
+1
source share

All Articles