sjk
sjk

Reputation: 19

How to make working CheckBox inside ListView (which has items from sqlite database)

I'm trying to make working CheckBox inside ListView. Layout is just main.xml which shows multiple row.xml when new information is added to sqlite database. I can make working CheckBox in main.xml, but I don't know how to make working CheckBox inside row.xml.

AndroidSQLite.java (shows main.xml, and row.xml inside it)

     public class AndroidSQLite extends Activity {
     (...)

    checkBoxMain = (CheckBox)findViewById(R.id.checkboxmain1);

    listContent = (ListView)findViewById(R.id.contentlist);

    mySQLiteAdapter = new SQLiteAdapter(this);
    mySQLiteAdapter.openToWrite();

    cursor = mySQLiteAdapter.queueAll();
    String[] from = new String[]{SQLiteAdapter._id, 
    SQLiteAdapter.KEY_NAME,
                            SQLiteAdapter.KEY_QUANTITY, 
    SQLiteAdapter.KEY_CHECKED};
    int[] to = new int[]{R.id.id, R.id.name, R.id.quantity, 
    R.id.checkboxmain};
    cursorAdapter =
            new SimpleCursorAdapter(this, R.layout.row, cursor, from, 
    to);
    listContent.setAdapter(cursorAdapter);

    listContent.setOnItemClickListener(listContentOnItemClickListener);


    buttonAdd.setOnClickListener(buttonAddOnClickListener);

    checkBoxMain.setOnClickListener(onCheckboxClicked);

}

CheckBox OnClickListener (AndroidSQLite.java)

CheckBox.OnClickListener onCheckboxClicked
        = new CheckBox.OnClickListener() {

    public void onClick(View v) {
        CheckBox checkBoxMain = (CheckBox) 
 findViewById(R.id.checkboxmain1);
        boolean checked = checkBoxMain.isChecked();
        if (checked) {
            Boolean data1 = checkBoxMain.isChecked();
            mySQLiteAdapter.insertChecked(data1);
            updateList();
        }
    }

};

ListView OnItemClickListener (AndroidSQLite.java)

  private ListView.OnItemClickListener listContentOnItemClickListener
        = new ListView.OnItemClickListener(){

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int 
    position,
                            long id) {


        Cursor cursor = (Cursor) parent.getItemAtPosition(position);
        final int item_id = 
   cursor.getInt(cursor.getColumnIndex(SQLiteAdapter._id));
        String item_name = 
   cursor.getString(cursor.getColumnIndex(SQLiteAdapter.KEY_NAME));
        String item_quantity = 
 cursor.getString(cursor.getColumnIndex(SQLiteAdapter.KEY_QUANTITY));

        AlertDialog.Builder myDialog
                = new AlertDialog.Builder(AndroidSQLite.this);
      // when item in row.xml is clicked alertdialog is shown
      // code of AlertDialog

 myDialog.show();
    }};

onDestroy and updateList() (AndroidSQLite.java)

@Override
protected void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    mySQLiteAdapter.close();
}



private void updateList(){
    cursor.requery();
}

main.xml

   <?xml version="1.0" encoding="utf-8"?>
   <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <TextView
        android:id="@+id/panelup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LIST SQ1"
        />

    <ListView
        android:id="@+id/contentlist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/panelup"
        android:layout_above="@id/paneldown"/>
    <CheckBox
        android:id="@+id/checkboxmain1"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:id="@+id/paneldown"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">
        <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            />
        <EditText
            android:id="@+id/quantity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            />
        <Spinner
            android:id="@+id/mu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:entries="@array/mu_values"
            android:layout_weight="2"
          />
        <Button
            android:id="@+id/add"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="+"
            />
    </LinearLayout>
    </RelativeLayout>

row.xml

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:id="@+id/layoutmain"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:padding="2dip"
        android:text="M"/>
    <TextView
        android:id="@+id/id"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:padding="2dip"
        android:paddingRight="10dip"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:padding="2dip"
        android:paddingRight="10dip"
        android:text="-" />
    <TextView
        android:id="@+id/name"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="2dip"/>
</LinearLayout>
<TextView
    android:id="@+id/quantity"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="2dip"/>
<CheckBox
    android:id="@+id/checkboxmain2"
    android:focusable="false"
    android:focusableInTouchMode="false"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
</LinearLayout>

(checkboxmain2 from row.xml have android:focusable and android:focusableInTouchMode set on "false" because if it's not false, ListView OnItemClickListener it's notr working)

SQLiteAdapter.java

public class SQLiteAdapter {
(...)
public SQLiteAdapter openToWrite() throws android.database.SQLException {
    sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null, 
MYDATABASE_VERSION);
    sqLiteDatabase = sqLiteHelper.getWritableDatabase();
    return this;
}


public void close(){
    sqLiteHelper.close();
}

public long insertChecked(boolean data1){

    ContentValues contentValues = new ContentValues();
    contentValues.put(KEY_CHECKED, data1);
    return sqLiteDatabase.insert(MYDATABASE_TABLE, null, contentValues);
}

public Cursor queueAll(){
    String[] columns = new String[]{_id, KEY_NAME, KEY_PRICE, 
KEY_QUANTITY, KEY_MU,
            KEY_PDATE, KEY_SHOP, KEY_CHECKED};
    Cursor cursor = sqLiteDatabase.query(MYDATABASE_TABLE, columns,
            null, null, null, null, null);

    return cursor;
}

screen

CheckBox on top, which is checked on screen is working CheckBox (checkboxmain1) which adds "1" to database

Is there any way to make response of CheckBoxes in ListView?

Upvotes: 0

Views: 84

Answers (1)

MikeT
MikeT

Reputation: 57043

The following is a relatively simple way of handling click events of views within an Item of a ListView.

To simply obtain the actual item that has been clicked to distinguish it from another Item you need a Custom Adapter, in this case a Custom Cursor Adapter, in which you can set the view's tag to an appropriate value (the id).

In conjunction with this you use the onClick attribute to specify the method to be called when the view (checkbox) is clicked, which is defined in the Activity.

The following is a working example based upon your code. It uses a simpler and more flexible method for managing the ListView i.e. manageListView

Note some of your code has been omitted (commented out) for convenience.

The Code

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/layoutmain"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:padding="2dip"
            android:text="M"/>
        <TextView
            android:id="@+id/id"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:padding="2dip"
            android:paddingRight="10dip"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:padding="2dip"
            android:paddingRight="10dip"
            android:text="-" />
        <TextView
            android:id="@+id/name"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="2dip"/>
    </LinearLayout>
    <TextView
        android:id="@+id/quantity"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="2dip"/>
    <CheckBox
        android:id="@+id/checkboxmain2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="ListViewCheckBoxHanlder"/>
</LinearLayout>
  • Note the changes made to the CheckBox

activity_main.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="match_parent"
    >
    <TextView
        android:id="@+id/panelup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LIST SQ1"
        />
    <ListView
        android:id="@+id/contentlist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/panelup"
        android:layout_above="@id/paneldown"/>
    <CheckBox
        android:id="@+id/checkboxmain1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:id="@+id/paneldown"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">
        <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            />
        <EditText
            android:id="@+id/quantity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            />
        <Spinner
            android:id="@+id/mu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:entries="@array/mu_values"
            android:layout_weight="2"
            />
        <Button
            android:id="@+id/add"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="+"
            />
    </LinearLayout>
</RelativeLayout>

AndroidSQLite.java

public class AndroidSQLite extends AppCompatActivity {

    CheckBox checkBoxMain;
    ListView listContent;
    Button buttonAdd;
    Cursor cursor;
    SQLiteAdapter mySQLiteAdapter;
    //SimpleCursorAdapter cursorAdapter; //<<<<<<<<<< NOT USED ANYMORE
    MyCursorAdapter myadapter; //<<<<<<<<<< Use a custom adapter that sets the tag of the checkbox to the respective id

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        checkBoxMain = (CheckBox)findViewById(R.id.checkboxmain1);
        listContent = (ListView)findViewById(R.id.contentlist);
        mySQLiteAdapter = new SQLiteAdapter(this);
        mySQLiteAdapter.openToWrite();
        manageListView(); //<<<<<<<<<< ADDED

        /* !!!!!!!!! COMMENTED OUT
        cursor = mySQLiteAdapter.queueAll();
        String[] from = new String[]{SQLiteAdapter._id,
                SQLiteAdapter.KEY_NAME,
                SQLiteAdapter.KEY_QUANTITY,
                SQLiteAdapter.KEY_CHECKED};
        int[] to = new int[]{R.id.id, R.id.name, R.id.quantity,
                R.id.checkboxmain2};
        cursorAdapter =
                new SimpleCursorAdapter(this, R.layout.row, cursor, from,
                        to,0);
        listContent.setAdapter(cursorAdapter);

        listContent.setOnItemClickListener(listContentOnItemClickListener);
        */
        //buttonAdd.setOnClickListener(buttonAddOnClickListener); //<<<<<<<<<<< you want this back in
    }

    //<<<<<<<<<< ADDED >>>>>>>>>>
    @Override
    protected void onResume() {
        super.onResume();
        manageListView(); //Refresh the List when resuming e.g. returning from another activity
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        cursor.close(); //<<<<<<<<<< SHOULD ALWAYS CLOSE CURSOR
        mySQLiteAdapter.close();
    }

    //<<<<<<<<<< NO LONGER USED >>>>>>>>>>
    private void updateList(){
        cursor = mySQLiteAdapter.queueAll();
        myadapter.swapCursor(cursor);
    }

    //<<<<<<<< NOTE NOT USED but you'd want this to be used
    CheckBox.OnClickListener onCheckboxClicked
            = new CheckBox.OnClickListener() {

        public void onClick(View v) {
            CheckBox checkBoxMain = (CheckBox)
                    findViewById(R.id.checkboxmain1);
            boolean checked = checkBoxMain.isChecked();
            if (checked) {
                Boolean data1 = checkBoxMain.isChecked();
                mySQLiteAdapter.insertChecked(data1);
                manageListView(); //<<<<<<<<<< refresh the ListView
            }
        }
    };

    //<<<<<<<<<<NOT USED>>>>>>>>>>
    private ListView.OnItemClickListener listContentOnItemClickListener
            = new ListView.OnItemClickListener(){

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int
                position,
                                long id) {
            Cursor cursor = (Cursor) parent.getItemAtPosition(position);
            final int item_id =
                    cursor.getInt(cursor.getColumnIndex(SQLiteAdapter._id));
            String item_name =
                    cursor.getString(cursor.getColumnIndex(SQLiteAdapter.KEY_NAME));
            String item_quantity =
                    cursor.getString(cursor.getColumnIndex(SQLiteAdapter.KEY_QUANTITY));

            AlertDialog.Builder myDialog
                    = new AlertDialog.Builder(AndroidSQLite.this);
            // when item in row.xml is clicked alertdialog is shown
            // code of AlertDialog
            myDialog.show();
            updateList();
        }
    };

    /**<<<<<<<<<< ADDED >>>>>>>>>>>
     * Manage the ListView building from new or refreshing the data
     */
    private void manageListView() {
        cursor = mySQLiteAdapter.queueAll(); // get the source data (cursor) for the listview
        if (myadapter == null) {
            myadapter = new MyCursorAdapter(this,cursor);
            listContent.setAdapter(myadapter);
        } else {
            myadapter.swapCursor(cursor);
        }
    }

    /**<<<<<<<<<< ADDED >>>>>>>>>>
     * Handle the CheckBox being clicked,
     * NOTE set in the layout
     * @param v     The View
     */
    public void ListViewCheckBoxHanlder(View v) {
        CheckBox cb = v.findViewById(R.id.checkboxmain2);
        Toast.makeText(this, "You clicked the CheckBox for ID " + (String) cb.getTag(), Toast.LENGTH_SHORT).show();
        int checked = 0;
        if (cb.isChecked()) {
            checked = 1;
        }
        long id = Long.valueOf((String) cb.getTag());
        mySQLiteAdapter.updateChecked(id,checked);
        manageListView();
    }
}
  • Note some code commented out has been replaced, some has been commented out for convenience.

MyCursorAdapter.java

public class MyCursorAdapter extends CursorAdapter {


    public MyCursorAdapter(Context context, Cursor c) {
        super(context, c, true);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = super.getView(position, convertView, parent);
        return v;
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return LayoutInflater.from(context).inflate(R.layout.row,parent,false);
    }

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

        //Note Cursor will be positioned appropriately
        TextView name = (TextView) view.findViewById(R.id.name);
        TextView id = (TextView) view.findViewById(R.id.id);
        TextView quantity = (TextView) view.findViewById(R.id.quantity);
        CheckBox cb = (CheckBox) view.findViewById(R.id.checkboxmain2);

        name.setText(cursor.getString(cursor.getColumnIndex(SQLiteAdapter.KEY_NAME)));
        id.setText(cursor.getString(cursor.getColumnIndex(SQLiteAdapter._id)));
        quantity.setText(cursor.getString(cursor.getColumnIndex(SQLiteAdapter.KEY_QUANTITY)));
        cb.setChecked(cursor.getInt(cursor.getColumnIndex(SQLiteAdapter.KEY_CHECKED)) > 0);
        cb.setTag(cursor.getString(cursor.getColumnIndex(SQLiteAdapter._id))); //<<<<<<<<<< SET TAG to the ID
    }
} 
  • Note the above is a new class.

SQliteAdapter.java

public class SQLiteAdapter {

    SQLiteDatabase sqLiteDatabase;
    SQLiteHelper sqLiteHelper;
    Context context;

    public static final String KEY_CHECKED = "checked";
    public static final String _id = BaseColumns._ID;
    public static final String KEY_NAME = "name";
    public static final String KEY_QUANTITY = "quantity";
    public static final String KEY_PRICE = "price";
    public static final String KEY_MU = "mu";
    public static final String KEY_PDATE = "pdate";
    public static final String KEY_SHOP = "shop";

    public SQLiteAdapter(Context context) {
        this.context = context;
        openToWrite();
    }

    public SQLiteAdapter openToWrite() throws android.database.SQLException {
        sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null,
                MYDATABASE_VERSION);
        sqLiteDatabase = sqLiteHelper.getWritableDatabase();
        return this;
    }


    public void close() {
        sqLiteHelper.close();
    }

    public long insertChecked(boolean data1) {

        ContentValues contentValues = new ContentValues();
        contentValues.put(KEY_CHECKED, data1);
        return sqLiteDatabase.insert(MYDATABASE_TABLE, null, contentValues);
    }

    public int updateChecked(long id,int check) {
        ContentValues cv = new ContentValues();
        cv.put(KEY_CHECKED,check);
        String whereclause = _id + "=?";
        String[] whereargs = new String[]{String.valueOf(id)};
        return sqLiteDatabase.update(MYDATABASE_TABLE,cv,whereclause,whereargs);
    }

    public Cursor queueAll() {
        String[] columns = new String[]{_id, KEY_NAME, KEY_PRICE,
                KEY_QUANTITY, KEY_MU,
                KEY_PDATE, KEY_SHOP, KEY_CHECKED};
        Cursor cursor = sqLiteDatabase.query(MYDATABASE_TABLE, columns,
                null, null, null, null, null);
        return cursor;
    }
}
  • Note this may well be different, it was created to cater for testing.

SQLiteHelper.java

public class SQLiteHelper extends SQLiteOpenHelper {

    public static final String MYDATABASE_NAME = "mydatabase";
    public static final int  MYDATABASE_VERSION = 1;
    public static final String MYDATABASE_TABLE = "mytable";

    SQLiteDatabase mDB;

    public SQLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,int version) {
        super(context, name, factory, version);
        mDB = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String crt_tbl_sql = "CREATE TABLE IF NOT EXISTS " + MYDATABASE_TABLE + "(" +
                SQLiteAdapter._id + " INTEGER PRIMARY KEY, " +
                SQLiteAdapter.KEY_NAME + " TEXT, " +
                SQLiteAdapter.KEY_SHOP + " TEXT, " +
                SQLiteAdapter.KEY_PDATE + " TEXT, " +
                SQLiteAdapter.KEY_PRICE + " REAL, " +
                SQLiteAdapter.KEY_QUANTITY + " INTEGER, " +
                SQLiteAdapter.KEY_MU + " TEXT, " +
                SQLiteAdapter.KEY_CHECKED + " INTEGER DEFAULT 0" +
                ")";
        db.execSQL(crt_tbl_sql);
        addSomeTestingData(db,10);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    private long addRow(String name, String shop, String pdate, double price, int quantity, String mu, SQLiteDatabase db) {
        ContentValues cv = new ContentValues();
        cv.put(SQLiteAdapter.KEY_NAME,name);
        cv.put(SQLiteAdapter.KEY_SHOP,shop);
        cv.put(SQLiteAdapter.KEY_PDATE,pdate);
        cv.put(SQLiteAdapter.KEY_PRICE,price);
        cv.put(SQLiteAdapter.KEY_QUANTITY,quantity);
        cv.put(SQLiteAdapter.KEY_MU,mu);
        return db.insert(MYDATABASE_TABLE,null,cv);
    }

    private void addSomeTestingData(SQLiteDatabase db, int number_to_add) {

        for (int i = 0; i < number_to_add;i++) {
            String suffix = String.valueOf(i);
            String day_in_month = suffix;
            if (i < 10) {
                day_in_month = "0" + day_in_month;
            }
            addRow(
                    "Test" + suffix,
                    "Shop" + suffix,
                    "2019-01-" + day_in_month,
                    10.5 + new Double(i * 3),
                    i * 4,
                    "mu" + suffix,
                    db
            );
        }
    }
}
  • Note this may well be different, it was created to cater for testing.

Result

The following is a screenshot taken after clicking a number of checkboxes so the Toast would be shown :-

enter image description here

And a screenshot after restarting the App (showing that the state has been retained i.e. database updated according to the checkboxes) :-

enter image description here

Upvotes: 2

Related Questions