I'm developing an android app which shows phone contact as ListView(used Cursoradapter).Now I Want to add checkbox to the listView ,My problem is How to Insert checkbox data into database, based on if it is checked or not?
In my database class, I have a function which use to add names and numbers to my database,
createntry(String number,String name) // in my database class
Should I invoke this function in my CursorAdapter class ?
Recently, I found out that I should use getView function,but unfortunately I have no idea about getView, My question are
1-I should use this function in My CursorAdapter or else?
2- how to implement this function?
My CursorAdapterClass
public class ContactCursorAdapterCT extends CursorAdapter {
public ContactCursorAdapterCT(Context context, Cursor c) {
super(context, c);
}
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView name = (TextView)view.findViewById(R.id.contactlistTV1);
name.setText(cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
TextView phone = (TextView)view.findViewById(R.id.contactlistTV2);
phone.setText(cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.lvct, parent, false);
bindView(v, context, cursor);
return v;
}
public View getView(final int pos, View inView, ViewGroup parent) { //getView
}
My activity class
public class Contacts extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
Cursor cursor = getContentResolver().query
(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,null, null);
startManagingCursor(cursor);
ContactCursorAdapterCT adapter= new ContactCursorAdapterCT
(Contacts.this, cursor);
ListView contactLV = (ListView) findViewById(R.id.listviewblcontactsDB);
contactLV.setAdapter(adapter);
My database Class
public long creatEntry(String inputnumber , String name) { // for add data
// TODO Auto-generated method stub
ContentValues cv= new ContentValues();
cv.put(KEY_NUMBER, inputnumber);
cv.put(N_NAME, name);
Log.v(inputnumber, "adding to Database");
return ourdatabase.insert(DATABASE_TABLE, null, cv);
}
Firstly, no you don’t need getView
. bindView
in conjunction with newView
is completely sufficient as a replacement for it, some would probably say even better. Moreover, you don’t need to call bindview in new view. here’s a restructuring of what should be there.
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.lvct, parent, false);
return v;
}
This is totally your call but no I don’t think that you should your createEntry
method into the adapter, at least in its methods. The thing is that methods in the adapter are called for each row of your listview, so you might have a lot happening redundantly and plus I personally find it wasteful to be making insertions into a database into in increments. rather I think you have no choice but to do it all at once, because what if someone unselects a checkbox? you delete your entry? not only is it wasteful but it would be too cumbersome to keep track of the cursor positions or _id
, you’d need to re-query every-time that something had been added. What you should do is maintain a list of what needs to be added to the database and bulk insert it when it’s done.
First you need to make an object holding the data that you want inserted. An object is the cleanest way cause you need to hold multiple pieces of information. Here it is very simple, you insert values you want into the constructor and then retrieve with the getter methods.
public class ContactObject {
private String name;
private String phone;
public ContactObject(String name, String phone) {
super();
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
Now you need an object to hold these objects when they’re checked. weird I know, but it much more convenient if they’re identified and can be iterated over and in general are batched together for referencing. I think this sort of task calls for the HashMap
. Make it in the constructor.
contactMap = new HashMap<Integer, ContactObject>(c.getCount());
Now it’s time to boogie. make methods for the checkbox to add and remove stuff from your HashMap
.
cbInsert.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (cbInsert.isChecked()) {
contactMap.put(cursor.getPosition(), new ContactObject(str_name, str_phone));
} else {
contactMap.remove(cursor.getPosition());
}
}
});
We’ve used the cursor position like an id for our objects within the HashMap
. And when our checkbox is unchecked and we want to remove the object that was put in, we can just refer that that identifier that we used. Someone more prudent might want to check if something is there at the position before removing, that’s at your discretion. Now we’re almost done. How do we convert our HashMap to entries in the database? You have to access a database object, loop through and then get at your object one by one. The question is now is where. You could do it right in the adapter, but I usually do something like this in my activity because in my cases I usually have a database already made for the activity for other tasks and I don’t like to make more objects than I’m pushed to. So, what we can do is finish up with a getter method in our adapter for our HashMap:
public HashMap<Integer, ContactObject> getContactMap() {
return contactMap;
}
Now I’d imagine that you’d do something like this when your app is leaving so here goes.
@Override
protected void onDestroy() {
super.onDestroy();
HashMap<Integer, ContactObject> contactMap = adapter.getContactMap();
DatabaseHelper db = new DatabaseHelper(this);
// iterate through hashmap
for (Map.Entry<Integer, ContactObject> entry : contactMap.entrySet()) {
Integer key = entry.getKey();
ContactObject value = entry.getValue();
db.creatEntry(key, value.getPhone(), value.getName());
}
db.close();
}
Now things look a little weird, what happened what did i do with your entry method?
public long creatEntry(Integer id, String inputnumber, String name) { // for add data
long lng;
String strId = id.toString();
String[] selectionArgs = {strId};
Cursor cursor = ourdatabase.query(DATABASE_TABLE, null, "other_id = ?", selectionArgs, null, null, null);
if (cursor.moveToFirst()) {
// it exists, i'd assume that you might not want anything else done here
lng = -1;
} else {
// it doesn't exist
ContentValues cv= new ContentValues();
cv.put(KEY_NUMBER, inputnumber);
cv.put(N_NAME, name);
cv.put(KEY_OTHERID, strId);
Log.v(inputnumber, "adding to Database");
lng = ourdatabase.insert(DATABASE_TABLE, null, cv);
}
// cursor.close();
return lng;
}
As you can i see I modified it so that it takes in the id as well. I thought that you an issue that you'd run into would be having repeats in your database. I thought you could manage it by having another field for an id that you can modify.This id refers to the id passed in from the HashMap
. I figured that every time that you make an insertion you first check if that previous id is there, then decide what you want to do. This is not a perfect solution but i just wanted to alert you that that issue is possible and give a possible hint as to manage it. In general the insert method should be fine if you only want to insert a couple rows but if you have a lot of stuff to insert, you might wanna look into bulk transactions for performance.
One more thing, checkboxes in your listview cannot be expected to have their states persist as you might normally expect it. You must explicitly dictate what state the checkbox has at each position. I made it correspond with if your HashMap
has something filled with its corresponding key. Here's the full adapter method in hopes that it's made clearer:
public class ContactCursorAdapterCT extends CursorAdapter {
private LayoutInflater inflater;
private HashMap<Integer, ContactObject> contactMap;
public ContactCursorAdapterCT(Context context, Cursor c) {
super(context, c);
inflater = LayoutInflater.from(context);
contactMap = new HashMap<Integer, ContactObject>(c.getCount());
// i used c.getCount() as a capacity limit for this.
// if you opened made this multiple times, it might get over inflated and
// slow things down.
}
@Override
public void bindView(View view, Context context, final Cursor cursor) {
TextView name = (TextView)view.findViewById(R.id.contactlistTV1);
TextView phone = (TextView)view.findViewById(R.id.contactlistTV2);
final CheckBox cbInsert = (CheckBox) view.findViewById(R.id.contactlistCB1);
String str_name = cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String str_phone = cursor.getString
(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
name.setText(str_name);
phone.setText(str_phone);
boolean isFilled = contactMap.containsKey(cursor.getPosition());
cbInsert.setChecked(isFilled);
// insert, remove objects to hashmap
cbInsert.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (cbInsert.isChecked()) {
contactMap.put(cursor.getPosition(), new ContactObject(str_name, str_phone));
} else {
contactMap.remove(cursor.getPosition());
}
}
});
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.lvct, parent, false);
return v;
}
public HashMap<Integer, ContactObject> getContactMap() {
return contactMap;
}
}