Thursday, March 24, 2011

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




android:layout_height=”match_parent”>
android:id=”@android:id/android:list”
android:layout_width=”match_parent”
android:layout_height=”0dip”
android:layout_weight=”1″
android:background=”#FFFFFF” />


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


android:layout_width=”fill_parent”

android:layout_height=”40sp”

android:orientation=”horizontal”

android:background=”#fff”>


android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:layout_alignParentRight=”true”

android:onClick=”clickHandler”>




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”>





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.sybase.mo.contacts;

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=MoDatabase.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