I am trying to update a boolean value in a database table, based on a Switch. A ListView holds a list of languages with an "Install" Switch button for each. My CursorAdapter is displaying the list and the switches. I've set OnCheckedChangeListeners for each Switch. When I select one of the Switches, it briefly shows it as being switched on, but immediately reverts back to "off." In my logging, I see that the value is set to "true" and that this is sent to the DatabaseProvider to update the database. But then immediately thereafter, I see another log output that the value is false, which is then sent to the DatabaseProvider in another update.
CursorAdapter:
public static class ViewHolder {
public final TextView nameView;
public final Switch installSwitch;
public ViewHolder(View view) {
nameView = (TextView) view.findViewById(R.id.language_textview);
installSwitch = (Switch) view.findViewById(R.id.installed_switch);
}
}
public LanguageAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item_language, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
return view;
}
@Override
public void bindView(View view, Context context, final Cursor cursor) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
long languageId = cursor.getLong(0);
String word = cursor.getString(1);
boolean langInstalled = Boolean.parseBoolean(cursor.getString(2));
viewHolder.nameView.setText(word);
viewHolder.installSwitch.setChecked(langInstalled);
viewHolder.installSwitch.setTag(languageId);
viewHolder.installSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
long languageId = (long) buttonView.getTag();
ContentValues cv = new ContentValues();
cv.put(TranslationContract.LanguageEntry.COLUMN_INSTALLED, isChecked);
Log.d(LOG_TAG, "onChecked, langID: " + languageId + " isChecked: " + isChecked);
buttonView.getContext().getContentResolver().update(TranslationContract.LanguageEntry.CONTENT_URI, cv, TranslationContract.LanguageEntry._ID+"=?", new String[] {String.valueOf(languageId)});
}
});
}
DatabaseProvider:
public int update(
Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsUpdated;
switch (match) {
case LANGUAGES:
rowsUpdated = db.update(LanguageEntry.TABLE_NAME, values, selection,
selectionArgs);
Log.d(LOG_TAG, "Updating languages: " + values.toString());
break;
}
Log:
07-13 21:39:32.785 LanguageAdapter﹕ onChecked, langID: 2 isChecked: true
07-13 21:39:32.800 DatabaseProvider﹕ Updating languages: installed=true
07-13 21:39:32.813 LanguageAdapter﹕ onChecked, langID: 2 isChecked: false
07-13 21:39:32.825 DatabaseProvider﹕ Updating languages: installed=false
After more digging, I've discovered two issues:
First, onCheckChanged seems to be called twice always, which is perhaps an android bug? See the following posts: onCheckedChanged fired multiple times, Listview with checkbox and CheckBox changes value twice
Instead, use onClickListener.
The second issue here is incorrect handling of boolean values between the DB cursor and the java code. SQLite doesn't have a native BOOLEAN type, so values are stored and returned as integers 0 or 1. They need to be evaluated and then converted to a java boolean value.
The corrected code would be:
@Override
public void bindView(View view, Context context, final Cursor cursor) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
long languageId = cursor.getLong(0);
String word = cursor.getString(1);
boolean langInstalled = cursor.getInt(2)>0; //convert DB 1 or 0 to java boolean
viewHolder.nameView.setText(word);
viewHolder.installSwitch.setChecked(langInstalled);
viewHolder.installSwitch.setTag(languageId);
viewHolder.installSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CompoundButton button = (CompoundButton) v;
long languageId = (long) button.getTag();
boolean isChecked = button.isChecked();
int dbBoolean = 0;
if (button.isChecked()) {
dbBoolean = 1;
}
ContentValues cv = new ContentValues();
cv.put(TranslationContract.LanguageEntry.COLUMN_INSTALLED, dbBoolean);
button.getContext().getContentResolver().update(TranslationContract.LanguageEntry.CONTENT_URI, cv, TranslationContract.LanguageEntry._ID + "=?", new String[]{String.valueOf(languageId)});
notifyDataSetChanged();
}
});
}