Android: Binding Data from a Database to a Checkbox in a Listview

Android: Binding data from a database to a CheckBox in a ListView?

I'm not sure how you would do this aside from creating a custom Adapter that overrode newView/bindView or getView, depending on what you override (ResourceCursorAdapter is a good one).

Ok, so here's an example. I didn't test to see if it would compile because I'm at work, but this should definitely point you in the right direction:

public class MyActivity extends ListActivity {

MyAdapter mListAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor myCur = null;

myCur = do_stuff_here_to_obtain_a_cursor_of_query_results();

mListAdapter = new MyAdapter(MyActivity.this, myCur);
setListAdapter(mListAdapter);
}


private class MyAdapter extends ResourceCursorAdapter {

public MyAdapter(Context context, Cursor cur) {
super(context, R.layout.mylist, cur);
}

@Override
public View newView(Context context, Cursor cur, ViewGroup parent) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return li.inflate(R.layout.mylist, parent, false);
}

@Override
public void bindView(View view, Context context, Cursor cur) {
TextView tvListText = (TextView)view.findViewById(R.id.list_text);
CheckBox cbListCheck = (CheckBox)view.findViewById(R.id.list_checkbox);

tvListText.setText(cur.getString(cur.getColumnIndex(Datenbank.DB_NAME)));
cbListCheck.setChecked((cur.getInt(cur.getColumnIndex(Datenbank.DB_STATE))==0? false:true))));
}
}
}

how to bind a checkbox to a listview

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.

how to implement a checkbox in each row in a Listview

You need to include the checkbox in the xml layout R.layout.notes_row. If you post your xml code we might be able to help you more.

How to make a checkbox from information found in DataBase?

here's an example based upon you code.

1) The Activity's layout (i.e a ListView with an id of lv001) as file activity_main.xml :-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mjt.budgetreport.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:layout_margin="10dp"
/>

<ListView
android:id="@+id/lv001"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>

</LinearLayout>

2) A layout for each row (Item in ListView terminology) as file listviewitem_record.xml

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

<TextView
android:id="@+id/record_price"
android:layout_width="0dp"
android:layout_weight="8"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/record_item"
android:layout_width="0dp"
android:layout_weight="10"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/record_details"
android:layout_width="0dp"
android:layout_weight="20"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/record_date"
android:layout_width="0dp"
android:layout_weight="10"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<CheckBox
android:id="@+id/record_checkbox"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
</LinearLayout>
  • Note! the inclusion of a CheckBox, as well as TextViews for all columns.

3) To simplify matters I used this as DatabaseHandler.java :-

public class DatabaseHandler extends SQLiteOpenHelper {

// All Static variables
// Database Version
private static final int DATABASE_VERSION = 4;

// Database Name
public static final String DATABASE_NAME = "Records_Item Purcashes";

// Contacts table name
public static final String TABLE_RECORDS = "Records";

// Contacts Table Columns names
public static final String KEY_ID = "_id";
public static final String KEY_PRICE = "Price";
public static final String KEY_ITEM = "Item";
public static final String KEY_DETAILS = "Details";
public static final String KEY_DATE = "Date";

public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

// Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
String CREATE_CONTACTS_TABLE = "CREATE TABLE " +
TABLE_RECORDS +
"(" +
KEY_ID + " INTEGER PRIMARY KEY," +
KEY_PRICE + " INTEGER," +
KEY_ITEM + " TEXT," +
KEY_DETAILS + " TEXT, " +
KEY_DATE + " TEXT" +
")";
db.execSQL(CREATE_CONTACTS_TABLE);
}

// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECORDS);

// Create tables again
onCreate(db);
}

public void insertRecord(int price, String item, String details, String date) {
ContentValues cv = new ContentValues();
cv.put(KEY_PRICE,price);
cv.put(KEY_ITEM,item);
cv.put(KEY_DETAILS,details);
cv.put(KEY_DATE,date);
SQLiteDatabase db = this.getWritableDatabase();
db.insert(TABLE_RECORDS,null,cv);
}

public Cursor getAllRecords() {
SQLiteDatabase db = this.getWritableDatabase();
return db.query(TABLE_RECORDS,null,null,null,null,null,null);
}
/*
// Adding new contact
public void addRecord(AlphabeticIndex.Record record) {
SQLiteDatabase db = this.getWritableDatabase();

ContentValues values = new ContentValues();

values.put(KEY_ID, record.getId()); // Contact Name
values.put(KEY_PRICE, record.getPrice()); // Contact Name
values.put(KEY_ITEM, record.getItem()); // Contact Name
values.put(KEY_DETAILS, record.getDetails()); // Contact Name
values.put(KEY_DATE, record.getDetails()); // Contact Phone Number

// Inserting Row
db.insert(TABLE_RECORDS, null, values);
db.close(); // Closing database connection
}
*/
/*
// Getting single contact
public AlphabeticIndex.Record getRecord(int id) {
SQLiteDatabase db = this.getReadableDatabase();

Cursor cursor = db.query(TABLE_RECORDS, new String[] { KEY_ID, KEY_PRICE,
KEY_ITEM, KEY_DETAILS, KEY_DATE }, KEY_ID + "=?",
new String[] { String.valueOf(id) }, null, null, null, null);
if (cursor != null)
cursor.moveToFirst();

AlphabeticIndex.Record record = new AlphabeticIndex.Record(Integer.parseInt(cursor.getString(0)),
Integer.parseInt(cursor.getString(1)), cursor.getString(2), cursor.getString(3), cursor.getString(4));
// return contact
return record;
}
*/

/*
// Getting All Contacts
public List<AlphabeticIndex.Record> getAllContacts() {
List<AlphabeticIndex.Record> contactList = new ArrayList<AlphabeticIndex.Record>();
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_RECORDS;

SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);

// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
AlphabeticIndex.Record record = new AlphabeticIndex.Record();
record.setId(Integer.parseInt(cursor.getString(0)));
record.setPrice(Integer.parseInt(cursor.getString(1)));
record.setItem(cursor.getString(2));
record.setDetails(cursor.getString(3));
record.setDate(cursor.getString(4));

// Adding contact to list
contactList.add(record);
} while (cursor.moveToNext());
}

// return contact list
return contactList;
}
*/

/*
// Getting contacts Count
public int getRecordsCount() {
String countQuery = "SELECT * FROM " + TABLE_RECORDS;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(countQuery, null);
// cursor.close();

// return count
return cursor.getCount();
}
*/

/*
// Updating single contact
public int updateContact(AlphabeticIndex.Record record) {
SQLiteDatabase db = this.getWritableDatabase();

ContentValues values = new ContentValues();
values.put(KEY_ID, record.getId());
values.put(KEY_PRICE, record.getPrice());
values.put(KEY_DETAILS, record.getDetails());
values.put(KEY_DATE, record.getDate());

// updating row
return db.update(TABLE_RECORDS, values, KEY_ID + " = ?",
new String[] { String.valueOf(record.getPrice()) });
}
*/
/*
// Deleting single contact
public void deleteContact(AlphabeticIndex.Record record) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_RECORDS, KEY_ID + " = ?",new String[]
{String.valueOf(record.getPrice()) });
db.close();
}
*/
}

Notes about Changes to DatabaseHandler.java

  • The rowid alias has been changed from ID to _id, this is because CursorAdapter's need a column named _id and that column should be an alias for for the rowid (not going into technicalities here).

  • Instead of using a Record class I've commented out your code that uses this class, for my convenience.

  • I've changed TABLE and COLUMN names definitions to public, so they can be accessed elsewhere.

  • I've added two new methods insertRecord and getAllRecords:-

    • insertRecord just to add some data for testing/example
    • getAllRecords retrieves all rows as a Cursor, as opposed to an array.
    • NOTE! The database is not closed, this would result in an exception because a Cursor needs access to the database (opening and closing a database can be detrimental anyway).

4) The Activity itself (just displays the ListView after adding some data for the first run) as file MainActivity.java :-

public class MainActivity extends AppCompatActivity {

DatabaseHandler mDBHandler;
ListView mListView;
SimpleCursorAdapter mSCA;
Cursor mCsr;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) this.findViewById(R.id.lv001); // Get the ListView from it's id
mDBHandler = new DatabaseHandler(this); // Get an instance of the Database Handler

// Add some data but only if there is no data
if (DatabaseUtils.queryNumEntries(mDBHandler.getWritableDatabase(),DatabaseHandler.TABLE_RECORDS) < 1) {

mDBHandler.insertRecord(100,"Rock","A large stone.","31/12/2017");
mDBHandler.insertRecord(50,"Boulder","A Rock.","31/12/2018");
mDBHandler.insertRecord(322,"Soil","Loose earth.","31/7/2015");
mDBHandler.insertRecord(237,"Stone","A small rock.","31/8/2014");
mDBHandler.insertRecord(32,"Pebble","A small smooth stone.","11/12/2017");
}

// get all rows into a Cursor
mCsr = mDBHandler.getAllRecords();

// Prepare a list of the columns to get the data from, for the ListViewt
String[] columns_to_get_data_from = new String[]{
DatabaseHandler.KEY_PRICE,
DatabaseHandler.KEY_ITEM,
DatabaseHandler.KEY_DETAILS,
DatabaseHandler.KEY_DATE
};

// Prepare a list of the Views into which to place the data
int[] itemviews_to_place_data_in = new int[]{
R.id.record_price,
R.id.record_item,
R.id.record_details,
R.id.record_date
};

// get and instance of SimpleCursorAdapter
mSCA = new SimpleCursorAdapter(this,
R.layout.listviewitem_record,
mCsr,
columns_to_get_data_from,
itemviews_to_place_data_in,
0);
// get and instance of SimpleCursorAdapter the listviewitem_record layout
mListView.setAdapter(mSCA);
}
}

Result :-

Sample Image

Notes

This doesn't go into handling the checkboxes, this would likely need a CustomAdapter. There's plenty of tutorials for that e.g. how do i create a custom cursor adapter for a listview for use with images and text?.

Edit Amended to Include CheckBox handling

1) The Activity's layout activity_main.xml - Added a Button for getting Checked Items :-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mjt.budgetreport.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:layout_margin="10dp"
/>

<Button
android:id="@+id/doitbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DO ALL CHECKED ROWS"
/>

<ListView
android:id="@+id/lv001"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>

</LinearLayout>

2) The Custom Cursor Adapter MyCustomCursorAdapter.java

  • Signature similar to SimpleCursorAdapter BUT with an extra parameter for the id of the CheckBox.
  • Includes method getCheckedRecordIdList, which will return a list of Id's that have been checked.
  • Issues a Toast when a checkbox is checked or unchecked which displays the item, as in the ListView's Item # (not the Item in the table), clicked and the Item clicked (as in the Item value from the table).

:-

public class MyCustomCursorAdapter extends CursorAdapter {

private Context mContext;
private Cursor mCsr;
private String[] mColumns;
private int[] mViews;
private int mLayout;
private boolean[] mCheckBoxStates;
private int mCheckBoxView;

// Constructor for the Custom Cursor Adapter
MyCustomCursorAdapter(Context context, int layout, Cursor csr, String[] from_columns, int[] to_views, int checkbox_view) {
super(context,csr, false);
mContext = context;
mLayout = layout;
mCsr = csr;
mColumns = from_columns;
mViews = to_views;
mCheckBoxView = checkbox_view;
}

@Override
// Inflate the layout we are going to use (as passed via 2nd parameter)
public View newView(Context context, Cursor csr, ViewGroup parent) {
// Initialise an int array for the checkboxes (all will be 0)
mCheckBoxStates = new boolean[csr.getCount()];
return LayoutInflater.from(context).inflate(mLayout,parent,false);
}

@Override
// Tie the from_columns to the display views
public void bindView(View view, Context context, Cursor csr) {

// Place the data from the cursor into the respective View (TextView)
for (int i = 0; i < mColumns.length; i++) {
((TextView) view.findViewById(mViews[i])).setText(csr.getString(csr.getColumnIndex(mColumns[i])));
}
// Set the checkbox (note should be false, unless otherwise changed)
CheckBox currentCheckBox = (CheckBox) view.findViewById(mCheckBoxView);
currentCheckBox.setChecked(mCheckBoxStates[mCsr.getPosition()]);
currentCheckBox.setTag(new Long(mCsr.getLong(mCsr.getColumnIndex(DatabaseHandler.KEY_ID))));

//
currentCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
final int position = mCsr.getPosition();
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Store the checkbox status
mCheckBoxStates[position] = ((CheckBox) buttonView).isChecked();
int restore_cursor_position = mCsr.getPosition();
//Move the Cursor to the respective row
//NOTE! 1st position in Lisview is 1 but equates to cursor row 0 etc hence -1
mCsr.moveToPosition(position);
Toast.makeText(mContext,
"You Changed the CheckBox for row " +
Integer.toString(position + 1) +
" Item is " +
mCsr.getString(mCsr.getColumnIndex(DatabaseHandler.KEY_ITEM))
,
Toast.LENGTH_SHORT
).show();
//restore the Cursor's position
mCsr.moveToPosition(restore_cursor_position);
}
});
}

// get the list of items (the ID's as long) that have been checked.
public long[] getCheckedRecordIdList() {

// using ArrayList as we can add as we don't yet know how many
ArrayList<Long> rva = new ArrayList<>();
// Just in case save the current position of the Cursor
int restore_cursor_position = mCsr.getPosition();
// Loop through the checkbox states
for (int i=0; i < mCheckBoxStates.length; i++) {
// If the checkbox reflected as being checked then handle, else ignore it
if (mCheckBoxStates[i]) {
// Move to the respective cursor row
mCsr.moveToPosition(i);
// get the respective ID and store in the arraylist
rva.add(mCsr.getLong(mCsr.getColumnIndex(DatabaseHandler.KEY_ID)));
}
}
// Done with the Cursor so re-position it
mCsr.moveToPosition(restore_cursor_position);

// Create the long array to be returned
long[] rv = new long[rva.size()];
// Populate the long array with the id's from the arraylist
for (int i=0; i < rva.size(); i++) {
rv[i] = rva.get(i);
}
// return the long[]
return rv;
}
}

3) The amended MainActivity to use the Custom Adapter and to get the list of checked Records (displays number selected via Toast) - MainActivity.java

  • Changed/Added Lines/Methods are marked with <<<<<.

:-

public class MainActivity extends AppCompatActivity {

DatabaseHandler mDBHandler;
ListView mListView;
SimpleCursorAdapter mSCA;
MyCustomCursorAdapter mMCA; // <<<<<
Cursor mCsr;
Button mDoItButton;
Context mContext; //<<<<<
long[] mCheckedIdList; //<<<<<

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
mListView = (ListView) this.findViewById(R.id.lv001); // Get the ListView from it's id
mDoItButton = (Button) this.findViewById(R.id.doitbutton); //<<<<<
mDBHandler = new DatabaseHandler(this); // Get an instance of the Database Handler

// Add some data but only if there is no data
if (DatabaseUtils.queryNumEntries(mDBHandler.getWritableDatabase(),DatabaseHandler.TABLE_RECORDS) < 1) {

mDBHandler.insertRecord(100,"Rock","A large stone.","31/12/2017");
mDBHandler.insertRecord(50,"Boulder","A Rock.","31/12/2018");
mDBHandler.insertRecord(322,"Soil","Loose earth.","31/7/2015");
mDBHandler.insertRecord(237,"Stone","A small rock.","31/8/2014");
mDBHandler.insertRecord(32,"Pebble","A small smooth stone.","11/12/2017");
}
mDBHandler.increasePrice(1,213);

// get all rows into a Cursor
mCsr = mDBHandler.getAllRecords();

// Prepare a list of the columns to get the data from, for the ListViewt
String[] columns_to_get_data_from = new String[]{
DatabaseHandler.KEY_ID,
DatabaseHandler.KEY_PRICE,
DatabaseHandler.KEY_ITEM,
DatabaseHandler.KEY_DETAILS,
DatabaseHandler.KEY_DATE
};

// Prepare a list of the Views into which to place the data
int[] itemviews_to_place_data_in = new int[]{
R.id.record_id,
R.id.record_price,
R.id.record_item,
R.id.record_details,
R.id.record_date
};

// get and instance of SimpleCursorAdapter the listviewitem_record layout
mSCA = new SimpleCursorAdapter(this,
R.layout.listviewitem_record,
mCsr,
columns_to_get_data_from,
itemviews_to_place_data_in,
0);
// Tie the adapter to the Listview
mListView.setAdapter(mSCA);

// <<<<<< Alternate Custom Cursor Adapter
mMCA = new MyCustomCursorAdapter(this,
R.layout.listviewitem_record,
mCsr,
columns_to_get_data_from,
itemviews_to_place_data_in,
R.id.record_checkbox //<<<<<< id of the checkbox
);
// Hijack the Listview
mListView.setAdapter(mMCA); //<<<<<<

// <<<<< DO IT BUTTON HANDLER i.e. get list of ID's for checked items
mDoItButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// get the ID's for the checked Items
mCheckedIdList = mMCA.getCheckedRecordIdList();
Toast.makeText(mContext,"You have " + mCheckedIdList.length + " Items checked.",Toast.LENGTH_SHORT).show();
}
});

}
}
  • Note! you wouldn't typically have two adapters, but I left the SimpleCursorAdapter in for comparison.

  • Note! be a little patient, as if you click too much too soon you may get consfused.

Note! See other answer for deleting checked items.

android how to dynamically bind checkboxes with custom listview

What if you use the default listview of android with checkbox.
By using :

setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, COUNTRIES));

where COUNTRIES is static final string array that contains the item to show..

Getting checkbox information from a listview

I got around the problem by Custom Adapter's bindView, I created an ArrayList<Integer> variable

ArrayList<Integer> mCheckedItems = new ArrayList<Integer>();

and In the bindView I set a checkedchangelistener on the checkbox to see if the box was checked or not. If it was checked I put the id from the database that the cursor got into the mCheckedItems Array

adapter:

public class CheckAdapter extends SimpleCursorAdapter{

Context context;

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

@Override
public void bindView(View view,Context context,Cursor cursor){
final String name = cursor.getString(cursor.getColumnIndex(BowlersDB.NAME));
final int id = cursor.getInt(cursor.getColumnIndex(BowlersDB.ID));
TextView tv = (TextView)view.findViewById(R.id.nameCheckTV);
tv.setText(name);

CheckBox cb = (CheckBox)view.findViewById(R.id.checkBox1);
cb.setOnCheckedChangeListener(new OnCheckedChangeListener(){

@Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

if(isChecked){
mCheckedItems.add(id);
}else if(!isChecked){
for(int i=0; i< mCheckedItems.size(); i++){
if(mCheckedItems.get(i) == id){
mCheckedItems.remove(i);
}
}
}

}

});
}

After the id was inserted into the array I used the array list to used them how I needed

ListActivity with checkbox in Android

You can extend the Adapter and override the bindView method, or call setViewBinder. A couple of detailed solutions to this are answered in a similar question here:

Android: Binding data from a database to a CheckBox in a ListView?



Related Topics



Leave a reply



Submit