Home > Uncategorized > Android ListView with CheckBox : Retain State

Android ListView with CheckBox : Retain State

I wish to create Custom ListView with CheckBox. I faced a problem to retain state of check box while scrolling. After searching a long for this , I came up with this solution.

I used Custom Cursor Adapter for this. I am attaching the code snippet for this.

First you need to create one xml in layout folder : Lets say it main.xml

<?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”>
<ListView
android:id=”@android:id/android:list”
android:layout_width=”match_parent”
android:layout_height=”0dip”
android:layout_weight=”1″
android:background=”#FFFFFF” />
</LinearLayout>

This main.xml will create the one Linear Layout with ListView. Now , we wish to create the custom xml which we will inflate to create the custom list view with check boxes.

custom.xml

<RelativeLayout  xmlns:android=”http://schemas.android.com/apk/res/android&#8221;

android:layout_width=”fill_parent”

android:layout_height=”40sp”

android:orientation=”horizontal”

android:background=”#fff”>

<CheckBox   android:id=”@+id/checkbox”

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:layout_alignParentRight=”true”

android:onClick=”clickHandler”>

</CheckBox>

<TextView

android:id=”@+id/textview”

android:textColor=”#000″

android:textSize=”18sp”

android:text=”Test”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:layout_alignParentLeft=”true”

android:layout_toLeftOf=”@id/checkbox”>

</TextView>

</RelativeLayout>

Now we need to create the one holder class to hold these views created for custom list view.

package com.avi.listview;
import android.view.View;

import android.widget.CheckBox;

import android.widget.TextView;

public class ViewHolder {

private TextView textView;

private CheckBox checkBox;

public View base;
public ViewHolder(View base){

this.base = base;

}

public TextView getTextView() {

return textView;

}
public void setTextView(TextView textView) {

this.textView = textView;

}
public CheckBox getCheckBox() {

return checkBox;

}
public void setCheckBox(CheckBox checkBox) {

this.checkBox = checkBox;

}

}

Now we have to create the database from which we can populate our list view:

There is one very important point : If you are using cursor adapter then you must create column “_id” in your table. Otherwise it will throw exception.

Now we will create database helper class to create the database:

AndroidContext.Java

package com.avi.listview;

import android.content.Context;

public class AndroidContext
{

private static Context s_oContext;
/**
* Sets the Android context used for file access. Should be set
* at application startup, before the log is used.
* @param oContext Application context
*/
public static synchronized void setContext( Context oContext )
{
s_oContext = oContext;
}

/**
* Sets the Android context used for file access. Should be set
* at application startup, before the log is used.
* @param oContext Application context
*/
public static synchronized Context getContext()
{
return s_oContext;
}
}

Now creating DatabaseHelper.java class

DatabaseHelper.java

package com.android.db;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {

private static DatabaseHelper o_instance = null;
public static final String DATABASE_NAME = “TestDb”;
public static final int DATABASE_VERSION = 1;
public SQLiteDatabase o_db = null;
public static final String USER_PASSWORD = “userpassword”;

public DatabaseHelper() {
super(AndroidContext.getContext(), DATABASE_NAME, null,
DATABASE_VERSION);
o_db = getWritableDatabase();
}

public static DatabaseHelper getInstance() {
if (o_instance == null) {
o_instance = new DatabaseHelper();
}
return o_instance;
}

@Override
public void onCreate(SQLiteDatabase db) {
final String[] creatStatments = new String[] { “create table ”
+ USER_PASSWORD
+ ” (_id INTEGER PRIMARY KEY,username TEXT,password TEXT,selected INTEGER DEFAULT 0)” };

for (String sStmt : creatStatments) {
db.execSQL(sStmt);
}

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(“DROP TABLE IF EXISTS “+USER_PASSWORD);
onCreate(db);
}

public SQLiteDatabase getDb() {
return o_db;
}

}

Now its time to create the CustomCursorAdapter

package com.avi.listview;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class CustomCursorAdapter extends CursorAdapter{

SQLiteDatabase dh=DatabaseHelper.getInstance().getDb();
private LayoutInflater mInflater;
private Context mContext;
Cursor cursor;
public CustomCursorAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
mInflater = LayoutInflater.from(context);
mContext = context;
cursor=c;
}

@Override
public void bindView(View view, Context context, final Cursor cursor) {
// TODO Auto-generated method stub
ViewHolder holder=(ViewHolder)view.getTag();

holder.setTextView((TextView)view.findViewById(R.id.textview));
holder.setCheckBox((CheckBox)view.findViewById(R.id.checkbox));
CheckBox cb=holder.getCheckBox();

holder.getTextView().setText( cursor.getString(cursor.getColumnIndex(“username”)));

cb.setTag(new Integer(cursor.getPosition()));

CompoundButton.OnCheckedChangeListener checkedChange= new CompoundButton.OnCheckedChangeListener() {

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO Auto-generated method stub

ContentValues contentValues=new ContentValues();

Integer currentPosition = (Integer)buttonView.getTag();

String currentPositionString=Double.toString(currentPosition);
if(cursor.moveToPosition(currentPosition))
{

String rowID=cursor.getString(cursor.getColumnIndex(“_id”));
if(isChecked){
contentValues.put(“selected”, “1”);
dh.update(DatabaseHelper.USER_PASSWORD, contentValues, “_id=?”, new String[]{rowID});

}else if(!isChecked){
contentValues.put(“selected”, “0”);
dh.update(DatabaseHelper.USER_PASSWORD, contentValues, “_id=?”, new String[]{rowID});
}

}

}
};
cb.setOnCheckedChangeListener(checkedChange);

if(cursor.getString(cursor.getColumnIndex(“selected”)).compareTo(“1”)==0)
{
cb.setChecked(true);
}
else
{
cb.setChecked(false);
}
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder;
View convertView = mInflater.inflate(R.layout.custom, parent,false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
return convertView;
}

}

Now we created the CustomCursorAdapter which will hold the custom row for list view and state of check box is maintain by the database column “selected” if it is 1 then it is checked otherwise unchecked.

Now its time to create our main activity which will show the final result.

MainActivity.java

import android.app.ListActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends ListActivity{

Cursor cursor;
SQLiteDatabase dh;
CustomCursorAdapter myCursorAdapter;
ContentValues values
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidContext.setContext(this);

dh=DatabaseHelper.getInstance().getDb();
values = new ContentValues();
// Inserting some data in SQLite to populate list view
insertData(“Avinash” , “123456”);
insertData(“Rakesh” ,”qwerty”);
insertData(“Prateek”,”onMobile”);
insertData(“Rajesh”,”Symphony”);
insertData(“Rahul”,”password123″);
insertData(“Kanishk”,”_smi1234″);
insertData(“Ahmad”,”asdfgh”);
insertData(“Akkie”,”zxcvbn”);
insertData(“Ankur”,”asdadd”);
insertData(“Rohit”,”bigb”);
insertData(“Abhi”,”rajjwe”);
insertData(“Sone”,”qwerty”);
createListView();
}

private void createListView() {
setContentView(R.layout.main);

cursor=dh.query(DatabaseHelper.USER_PASSWORD, new String[]{“_id”,”username”,”selected”}, null, null, null, null, “firstname asc”);

startManagingCursor(cursor);
myCursorAdapter= new CustomCursorAdapter(this,cursor);
this.getListView().setAdapter(myCursorAdapter);
}

private void insertData(String firstName ,String password){
if(values!= null){
values.clear();
}
values.put(“username”,firstName);
values.put(“password”, password);
dh.insert(DatabaseHelper.USER_PASSWORD, null, values);
}

private void clickHandler(View view){

if(view.getId() == R.id.checkbox){
cursor.requery(); /* to get the updated values from sqlite
on changing the check of checkbox*/
}
}
}

Try to follow the code. It will work for sure. May be this will give you some errors (But it should not) , in case try to remove them or you can ask and post your reviews. I will give my best to give answer of your queries

Advertisements
Categories: Uncategorized
  1. March 15, 2011 at 5:29 am

    good dear, keep it up

    • Avinash Nigam
      March 15, 2011 at 9:14 am

      Thanks..!!

  2. Rakesh
    March 15, 2011 at 9:14 am

    nice tutorial, please can u add camera tutorial.

  3. Rakesh
    March 15, 2011 at 9:31 am

    can u add the camera demo.

  4. Arun
    May 10, 2011 at 2:12 pm

    What is MoDatabase in CustomCursorAdapter class

  5. May 10, 2011 at 3:32 pm

    Hi Arun,
    Thanks for you query, Basically it is mistake, MoDatabase is basically DatabaseHelper class, where we declare all the database related query, creating Database, upgrade database.

  6. Mark
    September 3, 2011 at 2:19 am

    Finally! Everyone trying to solve this little puzzle always gives just a piece, and it has never worked. I was able to use your solution in the starting part of my app.

    Appreciate it.

  7. AndDev
    October 26, 2011 at 5:52 pm

    Hi! I write in an Android dev support forum and a user posted some code I guess copied from this post. He asked us to help him with this issue: everything ok if you scroll with the alredy checked items (states picked from the database), but the newly checked items didn’t mantain the state once you scroll away and return to them.
    This is caused by the cursor that has the old values of the database, so I just wanted to point out that your solution would work only if you requery the cursor everytime you check a new item (that is quite a slow and deprecated method). 🙂

  8. October 27, 2011 at 11:01 am

    HI AndDev,

    It will work each and everytime and its will use the basic feature of list, and everytime it will requery the cursor.

  9. Peter
    December 17, 2011 at 1:42 am

    A nice example, helped me a lot, eventhough I haven’t grasped all of the code yet. 😦

    How would one go about adding an OnItemClickListener on the list items, so that you as an example could list the usernames, click a list item and then start an intent to bring forth a view, where one would see further details about the user?

    Have used the folowing method in earlier projecst, but it doesn’t work because of the Cursor (or so I guess).

    public void onListItemClick(ListView parent, View view, int position, long id) {
    Intent intent = new Intent(this, UserDetails.class);
    Cursor cursor = (Cursor) adapter.getItem(position);
    //intent.putExtra(“USER_ID”, cursor.getInt(cursor.getColumnIndex(“_id”)));
    startActivity(intent);

    Sorry for the trouble.

  10. Thomas
    March 26, 2012 at 2:50 pm

    This is a a little more concise than what I used previously so I like this example. I used the following to register a click listener that can register click events on the list items.

    1) add an ID to the relative layout in custom.xml and make it clickable

    android:id="@+id/rv1"
    android:clickable="true"

    2) Create a RelativeLayout.onClickListener in the CustomCursorAdapter class


    // This listener listens for clicks on the listview rows
    public RelativeLayout.OnClickListener relativeLayoutListenter = new RelativeLayout.OnClickListener() {
    public void onClick(View relativeView) {

    switch (relativeView.getId()) {
    case R.id.rv1:

    TextView tv = (TextView) relativeView
    .findViewById(R.id.textview);
    Toast.makeText(mContext, tv.getText().toString(),
    Toast.LENGTH_SHORT).show();

    break;
    }
    }
    };

    3) Register the listener in the CustomCursorAdapter class’s bindView method.


    // make the listview rows selectable
    // rv1 must be android:clickable=true on relative layout
    RelativeLayout rv = (RelativeLayout) view.findViewById(R.id.rv1);
    rv.setOnClickListener(relativeLayoutListenter);

    At any rate, thanks for sharing.

  11. March 27, 2012 at 6:41 am

    Thanks to share. 🙂

  12. fuhur
    April 20, 2012 at 6:59 pm

    Hi, I am problem in the CustomCursorAdapter class.

    In this row:
    SQLiteDatabase dh=MoDatabase.getInstance().getDb();

    The error is “MoDatabase cannot be resolved”

    I think this is another class form com.kt.mo.contacts.
    How van I get it?

    Thanks

    Krisztián

    • April 21, 2012 at 7:23 am

      plz check the name, name is different.

    • April 21, 2012 at 8:49 am

      nameof MoDatabase is wrong plz change the name.

  13. April 21, 2012 at 8:54 am

    plz use DatabaseHelper instead of Modatabase

  14. Sejal
    September 27, 2012 at 7:16 am

    Thanks for the code but I have some problems with it.
    I pasted the whole code in as mentioned files, and then when I execute it I am getting a force to close error at the main activity itself, without getting any output.
    Is there something else to be taken care of apart from whatever is written here? manifest or anything else? If you could keep the downloadable code and/or screenshots of output then it would have helped a lot.
    I am working on android 2.2

    Thank you. Kindly Reply.

    • September 27, 2012 at 7:24 pm

      This code is working perfectly can you post your log info.

  15. Sejal
    September 30, 2012 at 4:23 pm

    09-30 16:11:22.418: I/DEBUG(30): debuggerd: Jun 30 2010 13:59:20
    09-30 16:11:22.868: D/qemud(37): entering main loop
    09-30 16:11:22.936: I/Vold(28): Vold 2.1 (the revenge) firing up
    09-30 16:11:22.996: D/Vold(28): Volume sdcard state changing -1 (Initializing) -> 0 (No-Media)
    09-30 16:11:23.088: I/Netd(29): Netd 1.0 starting
    09-30 16:11:24.157: W/Vold(28): No UMS switch available
    09-30 16:11:24.477: D/qemud(37): fdhandler_accept_event: accepting on fd 10
    09-30 16:11:24.477: D/qemud(37): created client 0xe078 listening on fd 8
    09-30 16:11:24.497: D/qemud(37): fdhandler_event: disconnect on fd 8
    09-30 16:11:24.626: D/qemud(37): fdhandler_accept_event: accepting on fd 10
    09-30 16:11:24.626: D/qemud(37): created client 0xf028 listening on fd 8
    09-30 16:11:24.626: D/qemud(37): client_fd_receive: attempting registration for service ‘gsm’
    09-30 16:11:24.626: D/qemud(37): client_fd_receive: -> received channel id 1
    09-30 16:11:24.637: D/qemud(37): client_registration: registration succeeded for client 1
    09-30 16:11:24.666: D/qemud(37): fdhandler_accept_event: accepting on fd 10
    09-30 16:11:24.666: D/qemud(37): created client 0x10fd8 listening on fd 11
    09-30 16:11:24.686: D/qemud(37): client_fd_receive: attempting registration for service ‘boot-properties’
    09-30 16:11:24.686: D/qemud(37): client_fd_receive: -> received channel id 2
    09-30 16:11:24.698: D/qemud(37): client_registration: registration succeeded for client 2
    09-30 16:11:24.698: I/qemu-props(50): connected to ‘boot-properties’ qemud service.
    09-30 16:11:24.706: I/qemu-props(50): received: dalvik.vm.heapsize=16m
    09-30 16:11:24.727: I/qemu-props(50): received: qemu.sf.lcd_density=160
    09-30 16:11:24.737: I/qemu-props(50): received: qemu.hw.mainkeys=1
    09-30 16:11:24.737: I/qemu-props(50): received: qemu.sf.fake_camera=back
    09-30 16:11:24.737: I/qemu-props(50): received:
    09-30 16:11:24.737: I/qemu-props(50): invalid format, ignored.
    09-30 16:11:28.097: D/AndroidRuntime(32): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
    09-30 16:11:28.097: D/AndroidRuntime(32): CheckJNI is ON
    09-30 16:11:30.737: I/(33): ServiceManager: 0xacd0
    09-30 16:11:30.737: D/AudioHardwareInterface(33): setMode(NORMAL)
    09-30 16:11:30.797: I/CameraService(33): CameraService started: pid=33
    09-30 16:11:30.857: I/AudioFlinger(33): AudioFlinger's thread 0xb3f0 ready to run
    09-30 16:11:33.177: D/dalvikvm(32): DexOpt: — BEGIN 'core.jar' (bootstrap=1) —
    09-30 16:11:41.376: D/dalvikvm(59): Ignoring duplicate verify attempt on Ljava/lang/Object;
    09-30 16:11:45.327: D/dalvikvm(59): Ignoring duplicate verify attempt on Ljava/lang/Class;
    09-30 16:12:20.276: D/dalvikvm(59): DexOpt: load 2769ms, verify 36760ms, opt 2149ms
    09-30 16:12:20.996: D/dalvikvm(32): DexOpt: — END 'core.jar' (success) —
    09-30 16:12:20.996: D/dalvikvm(32): DEX prep '/system/framework/core.jar': unzip in 4896ms, rewrite 47821ms
    09-30 16:12:21.406: D/dalvikvm(32): DexOpt: — BEGIN 'ext.jar' (bootstrap=1) —
    09-30 16:12:26.717: D/dalvikvm(67): DexOpt: load 368ms, verify 3879ms, opt 170ms
    09-30 16:12:26.806: D/dalvikvm(32): DexOpt: — END 'ext.jar' (success) —
    09-30 16:12:26.817: D/dalvikvm(32): DEX prep '/system/framework/ext.jar': unzip in 342ms, rewrite 5409ms
    09-30 16:12:31.616: D/dalvikvm(32): DexOpt: — BEGIN 'framework.jar' (bootstrap=1) —
    09-30 16:12:40.356: D/dalvikvm(68): Note: class Landroid/opengl/GLWrapperBase; has 211 unimplemented (abstract) methods
    : E/(): Device disconnected

    • Rakesh
      September 30, 2012 at 8:07 pm

      let me check your error msg.

  16. Kelvin
    October 7, 2012 at 3:03 pm

    Thanks for the code but I have some errors in my code, can anyone send the complete code for me? thanks

  17. deepak
    October 12, 2012 at 9:55 am

    The method getListView() is undefined for the type MainActivity
    please sole this

  18. December 17, 2012 at 3:41 am

    thanx for the tutorial but there is a little wrong code in MainActivity.java, method private void clickHandler(View view) can’t be found, you must change private to public to be found by environment.

    • February 26, 2013 at 9:39 am

      yes, thanks

  19. Mari
    January 2, 2013 at 10:37 pm

    May I get the source code for this example, please 🙂 ?
    My email is : s_mari20@yahoo.com

  20. February 19, 2013 at 11:35 am

    can I have the source code please ?
    thanks

  21. desi
    July 22, 2013 at 4:07 am

    can I have the source code please ?

    cz i’ve tried, but get errors following that code.. 😦

  22. March 4, 2014 at 4:14 pm

    Hi. Thanks a lot for your code, but it seems to have still another issue.
    In the MainActivity you should replace “firstname asc” by “username ASC”, because “firstname” is not the name of the column.
    Hope this help.

  23. July 11, 2016 at 9:23 pm

    cannot find symbol variable AndroidContext, on DatabaseHelper.java and MainActivity.java. Please help…….

  24. July 11, 2016 at 9:26 pm

    Cannot find the symbol AndroidContext in both DatabaseHelper.java and MainActivity.java Please any idea Ihave tried different alternatives but not working

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: