I have a listview containing on each row a textview with a checkbox, so when the checkbox is checked and we scroll down through the listview the checkbox instance will be taken from a place to another (reused..) and I have several checked checkboxes how to fix that I tried to bind the checkbox to the listview but that didn't work my code is:
SimpleCursorAdapter adapter =new SimpleCursorAdapter(this,R.layout.rating,cu,new String[]{"Title","Favorites"}, new int[]{R.id.text1,R.id.bt_rating},CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listv.setAdapter(adapter);
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder(){
/** Binds the Cursor column defined by the specified index to the specified view */
public boolean setViewValue(View view, Cursor cursor, int columnIndex){
if(view.getId() == R.id.bt_rating){
((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;
}
});
OnCheckedChangeListener myCheckChangList = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
buttonView.setChecked(isChecked);
}
};
My xml code of the content of the row of my listview is:
<?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"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/bt_rating"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:button="#android:drawable/btn_star"/>
<TextView
android:id="#+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/fsinlistview"
/>
</LinearLayout>
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:
Figure out which item was checked, and how that corresponds to your data
Update your data to suit the new checked/unchecked state
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.
In any Listview, the view are reused. As you scroll through the list, those that scroll up and off screen are recycled and used with newer information as it comes in below.
You need to keep track of your checkboxes with a sparse array. Mark the index in the array as cherked/unchecked as the user touches each. You then set the state of the checkbox based on the value in the array.
Here is some example code from an an older app, that does both a "select all" checkboxes as well as manages the entire list of those checked and unchecked. It was for a classroom attendance app I wrote, so it was much easier for a teacher to select "ALL" being in class and then unselect those that weren't present.
I have two checkboxes in this listview, and two sparse arrays, itemCheckedHere and itemCheckedLate (whether a student is in class, or was late).
public class MyDataAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private Long classnum;
private gradeBookDbAdapter mDbHelper;
public static final int LATE=2;
public static final int ATTEND=1;
int idxCol;
int idx;
// itemChecked will store the position of the checked items.
public MyDataAdapter(Context context, int layout, Cursor c, String[] from,
int[] to, Long mRowId) {
super(context, layout, c, from, to);
this.c = c;
this.context = context;
mDbHelper = new gradeBookDbAdapter(context);
mDbHelper.open();
classnum = mRowId;
c.moveToFirst();
}
public class ViewHolder{
public TextView text;
public TextView text2;
public ImageView image;
public CheckBox here;
public CheckBox late;
}
public View getView(final int pos, View inView, ViewGroup parent) {
Bitmap bm;
ImageView studentPhoto;
View vi=inView;
final ViewHolder holder;
if (inView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vi = inflater.inflate(R.layout.show_attendance, null);
holder=new ViewHolder();
holder.text=(TextView)vi.findViewById(R.id.stuname);
holder.text2=(TextView)vi.findViewById(R.id.stuIndex);
holder.image=(ImageView)vi.findViewById(R.id.icon);
holder.here=(CheckBox)vi.findViewById(R.id.attend);
holder.late=(CheckBox)vi.findViewById(R.id.late);
vi.setTag(holder);
}
else
holder=(ViewHolder)vi.getTag();
c.moveToPosition(pos);
int index = c.getColumnIndex(gradeBookDbAdapter.KEY_NAME);
String name = c.getString(index);
holder.text.setText(name);
index = c.getColumnIndex(gradeBookDbAdapter.KEY_ROWID);
String Index = c.getString(index);
holder.text2.setText(Index);
bm = gradeBookDbAdapter.getStudentPhoto(name);
if (bm != null) {
holder.image.setImageBitmap(bm);
}
else {
// use icon image
holder.image.setImageResource(R.drawable.person_icon);
}
// pull out existing attend/late fields and set accordingly
int attend = c.getInt(c.getColumnIndex(gradeBookDbAdapter.KEY_ATTEND));
if(attend==1){
holder.here.setChecked(true);
itemCheckedHere.set(pos, true);
}
//else {
// holder.here.setChecked(false);
// itemCheckedHere.set(pos, false);
//}
int late = c.getInt(c.getColumnIndex(gradeBookDbAdapter.KEY_LATE));
if (late==1){
holder.late.setChecked(true);
itemCheckedLate.set(pos, true);
}
//else {
// holder.late.setChecked(false);
// itemCheckedLate.set(pos, false);
//}
if (selectAllTouched) {
if(selectAll){
holder.here.setChecked(true);
itemCheckedHere.set(pos, true);
int who= new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(who, classnum, ATTEND, 1, attendDate );
}
else{
holder.here.setChecked(false);
itemCheckedHere.set(pos, false);
int who = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(who, classnum, ATTEND, 0, attendDate );
}
}
holder.here.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v.findViewById(R.id.attend);
if (cb.isChecked()) {
itemCheckedHere.set(pos, true);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, ATTEND, 1, attendDate );
} else if (!cb.isChecked()) {
itemCheckedHere.set(pos, false);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, ATTEND, 0, attendDate );
}
}
});
holder.late.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v.findViewById(R.id.late);
if (cb.isChecked()) {
itemCheckedLate.set(pos, true);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, LATE, 1, attendDate );
} else if (!cb.isChecked()) {
itemCheckedLate.set(pos, false);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, LATE, 0, attendDate );
}
}
});
holder.here.setChecked(itemCheckedHere.get(pos)); // this will Check or Uncheck the
holder.late.setChecked(itemCheckedLate.get(pos)); // this will Check or Uncheck the
// CheckBox in ListView
// according to their original
// position and CheckBox never
// loss his State when you
// Scroll the List Items.
return vi;
}
}
}
Related
I am having some serious issues with this, I have a listview that is populated with entries in a database, I have a custom row for each of the row in the listview (TextView1|TextView2|TextView3|Checkbox).
All I want to do is put a listener on each of the check boxes so when it is checked it is removed from the listview and deleted from the database. I have a function that deletes the row from the database when it is passed the value for Textview1.
The issue I am having is trying to get the row id from the checked box or even the textview value. I have searched everywhere but still cant get anything to work
CheckBox
Cursor cursor = db.getAllItems();
//String[] columns = new String[] {db.KEY_NAME, db.KEY_CODE, db.KEY_ROWID};
final String[] columns = new String[] {db.KEY_ITEM_NAME, db.KEY_MEASUREMENT, db.KEY_UNIT};
int[] to = new int[] {R.id.ingredientName, R.id.ingredientMeasurement, R.id.ingredientUnit};
final SimpleCursorAdapter myCursorAdapter = new SimpleCursorAdapter(this,R.layout.row4, cursor, columns, to, 0);
final ListView shoppingList = (ListView) findViewById(R.id.shoppingList);
shoppingList.setClickable(false);
//shoppingList.setChoiceMode(shoppingList.CHOICE_MODE_SINGLE);
shoppingList.setAdapter(myCursorAdapter);
CheckBox check = (CheckBox) findViewById(R.id.checkBox1);
check.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isChecked()) {
//how do i get the row_id or the text view value?
} else {
//do nothing
}
}
});
As far as I know it is not possible with this implementation. You have to create a custom adapter
public class CustomAdapter extends SimpleCursorAdapter{
public CustomAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
}
public View getView(final int position, View convertView, ViewGroup parent){
View view = super.getView(position, convertView, parent);
CheckBox check = (CheckBox) findViewById(R.id.checkBox1);
check.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isChecked()) {
// position will give you the position of the clicked element from where you can fetch your data
} else {
//do nothing
}
}
});
return view;
}
}
you can use it by
CustomAdapter adapter = new CustomAdapter(this, R.layout.row_layout, cursor, from, to);
list.setAdapter(adapter);
Use getTag() method.
For example, check.setTag( your wanted id) `
In listerner
int id =(Integer) Buttonview.getTag()
I was surprised that I couldn't find an existing answer on Stack that I could use for this, so here I am.
I have a ListFragment with a list attached to a SimpleCursorAdapter comprised of the rows defined by the following row.xml file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dip" >
<CheckBox
android:id="#+id/story_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:focusable="false"
android:focusableInTouchMode="false" />
<TextView
android:id="#+id/story"
android:layout_width="wrap_content"
android:layout_height="24sp"
android:lines="1"
android:scrollHorizontally="true"
android:singleLine="true"
android:layout_alignBaseline="#+id/story_check_box"
android:layout_alignBottom="#+id/story_check_box"
android:layout_toRightOf="#+id/story_check_box" />
</RelativeLayout>
I connect the list with the adapter with the following code in my ListFragment:
adapter = new SimpleCursorAdapter(getActivity(), R.layout.row, null, new String[] { CProvider.Stories.TITLE }, new int[] { R.id.story }, 0);
setListAdapter(adapter);
I then try to use a CheckBox in my fragment to toggle all the list checkboxes as follows:
CheckBox selectAll = (CheckBox) rootView.findViewById(R.id.select_check_box);
selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
final ListView listView = getListView();
for(int i=0; i < getListAdapter().getCount(); i++){
View view = getViewByPosition(i, listView);
CheckBox cb = (CheckBox)view.findViewById(R.id.story_check_box);
if (isChecked) {
cb.setChecked(true);
}
else {
cb.setChecked(false);
}
}
}
});
I got getViewByPositionfrom here: Get ListView children that are not in view, and that almost works, but a few of the checkboxes don't get checked (and there is a pattern to it, but I can't seem to figure it out). It also seems a bit kludgier than I would think is necessary.
I want the checkboxes on the left, so I don't want to use checkedtextviews. Maybe I need to extend CursorAdapter and override getView?
Thanks in advance.
Maybe I'm not correctly understanding your question but what I understood was that you wanted to check and uncheck all the checkboxes thanks to one "Select All checkbox".
Then, what I would do is to put the state of the "select all checkbox" as a variable of the class (as a boolean) which is overwritten by your selectAll.setOnCheckedChangeListener and say to the adapter "Hey, my state changed!" every time the checkbox changed its state.
Something like this:
class Dummy{
boolean isAllSelected = false;
Checkbox selectAll = (find or create your CheckBox)
selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) isAllSelected = true;
else isAllSelected = false;
listView.getAdapter().notifyDataSetChanged();
}
}
And then, you just have to override the getView() of this adapter (like you suggested) adding a "if (isAllSlected)" condition.
To me, it sounds the easiest to do but it's maybe not that good to call the notifyDataSetChanged() method every time the user clicks on a checkbox (it's not that efficient for so minor changes). Anyway, hope it helps (the code I wrote is maybe not with the correct syntax: I wrote it directly on the website form)!
Below is what I wound up doing. In addition to taking care of the "select all/ unselect all" functionality, it handles checking/unchecking a checkbox when the text of a list item is selected/unselected, and vice versa. I was concerned about getView being called frequently, but setItemChecked causes getView to be called no matter what, so there's a limit to how much calls to getView can be avoided. As ataulm mentioned in a comment, maybe a composite view would a solution with less fuss.
In onCreateView:
selectAllCheckBox = (CheckBox) rootView.findViewById(R.id.select_all_check_box);
selectAllCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
final ListView listView = getListView();
for(int i=0; i < getListAdapter().getCount(); i++){
listView.setItemChecked(i, isChecked);
}
}
});
I also created a custom SimpleCursorAdapter with the following code, which also uses a simple ViewHolder class. In getView I check which items in the list are selected and check the checkboxes corresponding to those items. There's also code that sets a list item as selected or not if its corresponding checkbox has been clicked (i.e., checked or unchecked).
class AvailableCursorAdapter extends SimpleCursorAdapter {
AvailableCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = super.getView(position, convertView, parent);
ViewHolder holder = (ViewHolder)row.getTag();
if (holder == null) {
holder = new ViewHolder(row);
row.setTag(holder);
}
holder.storyCheckBox.setChecked(false);
holder.story.setTextColor(Color.LTGRAY);
long [] checkedIds = getListView().getCheckedItemIds();
if (checkedIds != null) {
for (int i = 0; i < checkedIds.length; i++) {
if (checkedIds[i] == getListAdapter().getItemId(position)) {
holder.storyCheckBox.setChecked(true);
holder.story.setTextColor(Color.WHITE);
break;
}
}
}
final boolean isChecked = holder.storyCheckBox.isChecked();
holder.storyCheckBox.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getListView().setItemChecked(position, !isChecked);
}
});
return(row);
}
}
.
class ViewHolder {
CheckBox storyCheckBox;
TextView story = null;
ViewHolder(final View row) {
storyCheckBox = (CheckBox) row.findViewById(R.id.story_check_box);
story = (TextView) row.findViewById(R.id.story);
}
}
Finally, the following code causes getView to be called when a single ListItem is clicked, so that its corresponding checkbox gets selected or unselected, as appropriate:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
ViewHolder holder = (ViewHolder) v.getTag();
holder.storyCheckBox.setChecked(false);
holder.story.setTextColor(Color.LTGRAY);
long [] checkedIds = l.getCheckedItemIds();
if (checkedIds != null) {
for (int i = 0; i < checkedIds.length; i++) {
if (checkedIds[i] == getListAdapter().getItemId(position)) {
holder.storyCheckBox.setChecked(true);
holder.story.setTextColor(Color.WHITE);
break;
}
}
}
}
I have a SimpleCursorAdapter class, it gets various data from DB and displays in ListView. It implements an onclicklistener that sends a messageGuid String to another activity. All this works fine.
In the row of the ListView i have added a CheckBox and set an onCheckChangeListener to it. At the moment when the checkbox is checked, the messageGuid is always the last one in the cursor.
I need to find a way to get the listview row id of the row which hold the checkbox that has been checked. I can then get the correct cursor row and then in turn the correct messageGuid.
I've commented what i would like within the onCheckedChanged method.
Thanks in advance Matt.
private class MyAdapter extends SimpleCursorAdapter implements OnItemClickListener {
Cursor c;
String messageGuid;
public MyAdapter(Context context, int layout, Cursor c, String[] from,
int[] to) {
super(context, layout, c, from, to);
}
#Override
public
View getView(int position, View convertView, ViewGroup parent) {
Log.e(TAG, "inside myadapter getview for messages");
View v = super.getView(position, convertView, parent);
if(v == null)
return null;
c = (Cursor)getItem(position);
Log.e(TAG, "(Cursor)getItem(position) = " + c + "position = " + position);
v.setTag(c);
//other code removed, not relevant
String messageSender = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_SENDER));
String isRepliedTo = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_REPLIED));
String isStandAlone = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_IS_STANDALONE));
((TextView)v.findViewById(R.id.messagecreatedat)).setText(formattedMessCreatedAt );
((TextView)v.findViewById(R.id.messagetext)).setText(messageText);
((TextView)v.findViewById(R.id.messagesender)).setText(messageSender);
//#003F87 = blue
((TextView)v.findViewById(R.id.messagecreatedat)).setTextColor(Color.parseColor("#003F87"));
((TextView)v.findViewById(R.id.messagesender)).setTextColor(Color.parseColor("#003F87"));
((TextView)v.findViewById(R.id.messagetext)).setTextColor(Color.parseColor("#FF0000"));
CheckBox cb = ((CheckBox)v.findViewById(R.id.list_checkbox));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//i'd like to something like below where i can specify the row which contains
//the checkbox that has been check and map that to the row in the cursor.
// So if the checkbox in the 2nd row in the listview has been clicked then the messageGuid from the 2nd row in the cursor is found
//c.moveToPosition(the row position of the listview which holds the checkbox that has been clicked );
messageGuid = null;
messageGuid = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_GUID));
if(isChecked == true){
Log.e(TAG, "checkBox true and guid = " + messageGuid);
}else{
Log.e(TAG, "checkBox false and guid = " + messageGuid);
}
}
});
return v;
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int pos,
long id) {
Cursor itemCursor = (Cursor) view.getTag();
String messageGuid = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_GUID));
String messageText = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_TEXT));
String messageCreatedAt = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_CREATED_AT));
String messageSender = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_SENDER));
String messageReplied = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_REPLIED));
String messageSeen = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_SEEN));
String isStandAlone = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_IS_STANDALONE));
Intent i = new Intent(ViewMessagesActivity.this, ReplyToMessageActivity.class);
i.putExtra("guid", messageGuid);
i.putExtra("message", messageText);
i.putExtra("createdat", messageCreatedAt);
i.putExtra("sender", messageSender);
i.putExtra("messagereplied", messageReplied);
i.putExtra("messageseen", messageSeen);
i.putExtra("isstandalone", isStandAlone);
startActivity(i);
}
}// end of adapter
Make position final and use that on onCheckedChanged
or
make cb final
Add before cb.setOnCheckedChangeListener
cb.setTag(position);
And in public void onCheckedChanged you can retrieve the position
int pos = (Integer) cb.getTag();
I have created a listview which displays the installed applications. It has app icon + app name + check box. I want to select apps from this list and store the app names in a sqlite DB how should I begin with this?
Like how to identify the selected apps?
here's an adapter I made for one of my apps. This is a classroom attendance function that shows the student's name, photo and a couple of checkboxes (one for here, for for late). The list comes from a SQlite query, but I need to do something with each checkbox that is clicked.
public class MyDataAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
private Long classnum;
private gradeBookDbAdapter mDbHelper;
public static final int LATE=2;
public static final int ATTEND=1;
int idxCol;
int idx;
// itemChecked will store the position of the checked items.
public MyDataAdapter(Context context, int layout, Cursor c, String[] from,
int[] to, Long mRowId) {
super(context, layout, c, from, to);
this.c = c;
this.context = context;
mDbHelper = new gradeBookDbAdapter(context);
mDbHelper.open();
classnum = mRowId;
c.moveToFirst();
}
public class ViewHolder{
public TextView text;
public TextView text2;
public ImageView image;
public CheckBox here;
public CheckBox late;
}
public View getView(final int pos, View inView, ViewGroup parent) {
Bitmap bm;
ImageView studentPhoto;
View vi=inView;
final ViewHolder holder;
if (inView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vi = inflater.inflate(R.layout.show_attendance, null);
holder=new ViewHolder();
holder.text=(TextView)vi.findViewById(R.id.stuname);
holder.text2=(TextView)vi.findViewById(R.id.stuIndex);
holder.image=(ImageView)vi.findViewById(R.id.icon);
holder.here=(CheckBox)vi.findViewById(R.id.attend);
holder.late=(CheckBox)vi.findViewById(R.id.late);
vi.setTag(holder);
}
else
holder=(ViewHolder)vi.getTag();
c.moveToPosition(pos);
int index = c.getColumnIndex(gradeBookDbAdapter.KEY_NAME);
String name = c.getString(index);
holder.text.setText(name);
index = c.getColumnIndex(gradeBookDbAdapter.KEY_ROWID);
String Index = c.getString(index);
holder.text2.setText(Index);
bm = gradeBookDbAdapter.getStudentPhoto(name);
if (bm != null) {
holder.image.setImageBitmap(bm);
}
else {
// use icon image
holder.image.setImageResource(R.drawable.person_icon);
}
// pull out existing attend/late fields and set accordingly
int attend = c.getInt(c.getColumnIndex(gradeBookDbAdapter.KEY_ATTEND));
if(attend==1){
holder.here.setChecked(true);
itemCheckedHere.set(pos, true);
}
//else {
// holder.here.setChecked(false);
// itemCheckedHere.set(pos, false);
//}
int late = c.getInt(c.getColumnIndex(gradeBookDbAdapter.KEY_LATE));
if (late==1){
holder.late.setChecked(true);
itemCheckedLate.set(pos, true);
}
//else {
// holder.late.setChecked(false);
// itemCheckedLate.set(pos, false);
//}
holder.here.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v.findViewById(R.id.attend);
if (cb.isChecked()) {
itemCheckedHere.set(pos, true);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, ATTEND, 1, attendDate );
} else if (!cb.isChecked()) {
itemCheckedHere.set(pos, false);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, ATTEND, 0, attendDate );
}
}
});
holder.late.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v.findViewById(R.id.late);
if (cb.isChecked()) {
itemCheckedLate.set(pos, true);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, LATE, 1, attendDate );
} else if (!cb.isChecked()) {
itemCheckedLate.set(pos, false);
int Index = new Integer(holder.text2.getText().toString());
mDbHelper.updateAttend(Index, classnum, LATE, 0, attendDate );
}
}
});
holder.here.setChecked(itemCheckedHere.get(pos)); // this will Check or Uncheck the
holder.late.setChecked(itemCheckedLate.get(pos)); // this will Check or Uncheck the
// CheckBox in ListView
// according to their original
// position and CheckBox never
// loss his State when you
// Scroll the List Items.
return vi;
}
}
Notice I have two sparse arrays "ItemCheckedHere' and "ItemCheckLate". these keep track of which items I've selected. Android doesn't keep track and will reuse a view, so scrolling a list of checkboxes will result in boxes getting checked or unchecked seemingly at random without these arrays I use to maintain the checked status.
you'll need to make a custom listView adapter. You can make each item selectable and then when the user selects an app, you can call the code to insert it into your DB
I have an issue with Spinners in a ListView. I have a ListView with a CheckBox, a label, and two Spinners. The Spinner are populated from SQLite and that is working fine. I am not using the ViewHolder method because so far when the ListView row is clicked the CheckBoxes are checked or unchecked and the change is immediately saved to the database. When the row is checked the Spinners are made visible but are not visible when the row is not checked.
So the issue that I haven't managed to find a solution for is that I have no idea how to get the actual Spinner or even get the ListItem row that the clicked Spinner is on. The Activity extends ListActivity. Anyone know a way I can do this without using a ViewHolder or do I have to use a ViewHolder?
Here is the code that declares and populates the ListView:
mSsCursor = mDbHelper.fetchAllSsPlaylistSs(mPlId);
startManagingCursor(mSsCursor);
String[] from = new String[]{"pl_selected", BTDbAdapter.KEY_NAME, BTDbAdapter.KEY_NAME2};
int[] to = new int[]{R.id.pl_selected, R.id.name, R.id.name2};
mAllSs = new SimpleCursorAdapter(this, R.layout.pl_edit_ss_row, mSsCursor, from, to);
mAllSs.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
//custom handling of setting the value
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if(columnIndex == 3) {
ViewGroup row = (ViewGroup)view.getParent().getParent();
mSId = cursor.getInt(0);
if (cursor.getInt(3) > 0) {
mCheckBox = (CheckBox) row.findViewById(R.id.pl_selected);
mCheckBox.setChecked(true);
mTSpin = (Spinner) row.findViewById(R.id.pl_t_spin);
mMSpin = (Spinner) row.findViewById(R.id.pl_m_spin);
mtvT = (TextView) row.findViewById(R.id.pl_t);
mtvM = (TextView) row.findViewById(R.id.pl_m);
mTSpin.setVisibility(View.VISIBLE);
mtvT.setVisibility(View.VISIBLE);
mMSpin.setVisibility(View.VISIBLE);
mtvM.setVisibility(View.VISIBLE);
//set the values in the t spinner
PopulateTSpinner(cursor.getInt(4));
//set the values in the m spinner
PopulateMSpinner(cursor.getInt(5));
}
else {
mCheckBox = (CheckBox) row.findViewById(R.id.pl_selected);
mCheckBox.setChecked(false);
mTSpin = (Spinner) row.findViewById(R.id.pl_t_spin);
mMSpin = (Spinner) row.findViewById(R.id.pl_m_spin);
mtvT = (TextView) row.findViewById(R.id.pl_t);
mtvM = (TextView) row.findViewById(R.id.pl_m);
mTSpin.setVisibility(View.GONE);
mtvT.setVisibility(View.GONE);
mMSpin.setVisibility(View.GONE);
mtvM.setVisibility(View.GONE);
}
return true;
}
return false;
}
});
setListAdapter(mAllSs);
Thanks.
I don't know if I understood your question: If your app flow is:
show a list of data(CheckBox + TextView(Spinners hidden)) ->
user clicks a row(the Spinners appear for that row with(individual) data) ->
user selects something in those Spinners->
save that selection in the database
then I think you should go with a custom adapter and take care yourself of the row creation + data binding(I don't see how you would set a listener for the Spinners). Below is a small example on how you might do this(although probably not a pretty way of doing it):
public class CustomAdapter extends SimpleCursorAdapter {
private LayoutInflater mInflater;
public CustomAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
mInflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag(); // the holder
// pattern
// set the text for the TextView in your row
holder.name
.setText(cursor.getString(cursor.getColumnIndex("name")));
// status of the CheckBox from the database
int status = cursor.getInt(cursor.getColumnIndex("pl_selected"));
// set the CheckBox status
holder.ckb.setChecked((status > 0) ? true : false);
// get the id of this particular row, we'll use this later in the
// Spinner's listeners
long theId = cursor.getLong(cursor.getColumnIndex("_id"));
// see if it is time to show the Spinners
if (status > 0) {
// it is time to show the Spinners. Here you would do stuff
// like: setting up the Spinner's adapters + setting the
// listener
// I used a Spinner with entries set in the xml layout(so my
// selection result is a String)
holder.spin1.setVisibility(View.VISIBLE);
holder.spin2.setVisibility(View.VISIBLE);
// set theId as a tag so you know which Spinner was acted on
holder.spin1.setTag(new Long(theId));
holder.spin1
.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent,
View view, int position, long id) {
Long realRowId = (Long) parent.getTag();
// I don't know
ContentValues cv = new ContentValues();
// the column where I saved the spinner selected
// item is called "saved_item"
cv.put("saved_item", (String) parent
.getItemAtPosition(position));
// mDb is my SQLiteDatabase instance
mDb.update("tbl", cv, "_id = ?",
new String[] { String
.valueOf(realRowId) });
// I don't know how you saved the data, the
// above is just an example
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
// also implement the second Spinner like the first one
} else {
// required to prevent a recycled View from causing damage
holder.spin1.setVisibility(View.GONE);
holder.spin2.setVisibility(View.GONE);
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = mInflater.inflate(R.layout.adapters_listspinner_row,
parent, false);
ViewHolder holder = new ViewHolder();
holder.spin1 = (Spinner) v.findViewById(R.id.spinner1);
holder.spin1.setFocusable(false);
holder.spin2 = (Spinner) v.findViewById(R.id.spinner2);
holder.spin2.setFocusable(false);
holder.name = (TextView) v.findViewById(R.id.textView1);
holder.ckb = (CheckBox) v.findViewById(R.id.checkBox1);
holder.ckb.setFocusable(false);
v.setTag(holder);
return v;
}
class ViewHolder {
Spinner spin1, spin2;
TextView name;
CheckBox ckb;
}
}
Also, the required onListItemcClick method:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// manage the CheckBox state
CheckBox ckb = (CheckBox) v.findViewById(R.id.checkBox1);
ckb.setChecked(!ckb.isChecked());
ContentValues cv = new ContentValues();
cv.put("pl_selected", ckb.isChecked() ? 1 : 0);
mDb.update("tbl", cv, "_id = ?",
new String[] { String.valueOf(id) });
// requery the database so the changes are seen by the adapter, this is horrible!
Cursor re = mDb.query("tbl", null, null, null, null, null, null);
mAllSs.changeCursor(re);
}
As an advice, maybe you could modify the layout of your app and move the Spinners out of the ListView row.