Android: SimpleCursorAdapter usage - android

I have ONE annoying problem with SimpleCursorAdapter. My programm has list view and ListActivity. Each row has it's own layout:
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_width="fill_parent"
android:orientation="horizontal" android:weightSum="1.0">
<TableRow>
<TextView android:id="#+id/task_time"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textSize="24sp" android:text="Time">
</TextView>
<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="fill_parent">
<TextView android:id="#+id/task_name"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textSize="20sp" android:text="Name">
</TextView>
<TextView android:id="#+id/task_categoty"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Category" android:textSize="12sp">
</TextView>
</LinearLayout>
<TextView android:id="#+id/task_state"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="State" android:textSize="12sp">
</TextView>
<CheckBox android:id="#+id/task_enabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:focusable="false">
</CheckBox>
</TableRow>
Tasks are stored in SQLite database. I have DAO object (singleton) to access the database.
TaskDao:
public void updateEnabled(int id, boolean enabled){
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(ENABLED_COLUMN, enabled==true?1:0);
Log.i(TAG, "update to " + cv.get(ENABLED_COLUMN) );
try{
db.beginTransaction();
db.update(TASK_TABLE, cv, ID_COLUMN+"=?", new String[]{id+""});
db.setTransactionSuccessful();
} catch (SQLException e) {
Log.i(TAG, "edit task failed!");
} finally {
db.endTransaction();
if (db != null)
db.close();
}
}
and the Cursor method for ListActivity:
public Cursor getTasks(){
SQLiteDatabase db = dbHelper.getReadableDatabase();
return db.query(TASK_TABLE, COLUMNS, null, null, null, null, NAME_COLUMN);
}
I extended SimpleCursorAdapter (TaskDbAdapter) like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
convertView = inflater.inflate(R.layout.task_list_row, null);
}
Cursor c = getCursor();
c.moveToPosition(position);
Log.i(TAG, "getView " + position + " = " + c.getInt(enabledIdx));
enabled.setTag(c.getInt(c.getColumnIndex(BaseColumns._ID)));
enabled.setChecked(c.getInt(enabledIdx)>0?true:false);
enabled.setOnClickListener(this);
return convertView;
}
#Override
public void onClick(View v) {
CheckBox box = (CheckBox) v;
Integer id = (Integer)box.getTag();
TaskDao.getInstance(context).updateEnabled(id.intValue(), box.isChecked());
}
And at last I use all the above stuff in my main ListActivity
private void refreshList(){
c = TaskDao.getInstance(this).getTasks();
startManagingCursor(c);
adapter = new TaskDbAdapter(this, R.layout.task_list_row, c, new String[]{TaskDao.ENABLED_COLUMN}, new int[]{R.id.task_enabled});
setListAdapter(adapter);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task);
getListView().setItemsCanFocus(false);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
getListView().setVerticalScrollBarEnabled(true);
registerForContextMenu(getListView());
getListView().setOnCreateContextMenuListener(this);
refreshList();
}
#Override
protected void onResume() {
super.onResume();
refreshList();
}
#Override
protected void onPause() {
super.onPause();
}
Everything works fine. But CheckBoxes loose their states. For instance I check my first column and scroll the list down. In my trace before press I have:
getView 0 = 0
getView 2 = 0
getView 3 = 0
then
uptate to 1
and then (when I scroll up to the first element)
getView 0 = 0
getView 2 = 0
getView 3 = 0
I tried to make getCursor().requery(); in my TaskDbAdapter onClick method. But then I saw no items in the list! And exception because of cursor management(connection was closed by android). When I write startManagingCursor(c); in refreshList() method then check and uncheck methods don't work.
Please, Help!

I didn't read all your source so my suggestion may be totally wrong, but I will give a try.
Take a look at the documentation of BaseAdapter class.
public void notifyDataSetChanged ()
may do the work.
You also can register Observer for this...
public void registerDataSetObserver (DataSetObserver observer)

I struggled with this as well. I ended up storing all checked boxes in the db as either 0 or 1. Then I check their state from the database to determine if they are marked or not.
public class DetailCursorAdapter extends SimpleCursorAdapter {
private Cursor c;
private Context context;
public DetailCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.c = c;
this.context = context;
}
public View getView(int pos, View inView, ViewGroup parent) {
View v = inView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.check_list, null);
}
Log.i("pos = ..................", "pos = "+pos);
this.c.moveToPosition(pos);
//this.c.moveToPosition(this.c.getInt(this.c.getColumnIndex("_id")));
CheckBox cBox = (CheckBox) v.findViewById(R.id.bcheck);
cBox.setTag(this.c.getInt(this.c.getColumnIndex("_id")));
/*
* when reloading the list, check for chkd status, this is broken. Need to query db directly.
*/
EventDbAdapter mDbHelper = new EventDbAdapter(context);
mDbHelper.open();
int idTag = (Integer) cBox.getTag();
int checked = mDbHelper.selectChk(idTag);
mDbHelper.close();
Log.i("results from selectChk.....................", ""+checked);
if (checked == 1) {
cBox.setChecked(true);
} else {
cBox.setChecked(false);
}
/*
* Populate the list
*/
TextView txtdateTime = (TextView)v.findViewById(R.id.time);
txtdateTime.setText(this.c.getString(this.c.getColumnIndex("time")));
TextView txtdateEvent = (TextView)v.findViewById(R.id.event);
txtdateEvent.setText(this.c.getString(this.c.getColumnIndex("event")));
TextView txtdateLocation = (TextView)v.findViewById(R.id.location);
txtdateLocation.setText(this.c.getString(this.c.getColumnIndex("location")));
ImageView arrow = (ImageView) v.findViewById(R.id.arrowId);
arrow.setImageResource(R.drawable.rightarrow);
Log.i("if chk in db is = 1 then set checked.........",this.c.getString(this.c.getColumnIndex("checked")) +" " +this.c.getString(this.c.getColumnIndex("time")));
/*
* Controls action based on clicked list item (background)
*/
View lv = v.getRootView();
lv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View lv) {
CheckBox cBox = (CheckBox) lv.findViewById(R.id.bcheck);
// id holds the rowid of each event. pass this to a new activity to query for description
// Call Event Detail
String id = cBox.getTag().toString();
Intent i = new Intent(context, EventDetail.class);
//i.putExtra("description", c.getString(c.getColumnIndex("description")));
i.putExtra("_id", id);
context.startActivity(i);
}
});
/*
* Begin - Controls action based on clicked Text only
txtdateEvent.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
CharSequence charseq = "Darth Vader is alive";
Toast.makeText(context, charseq, Toast.LENGTH_SHORT).show();
}
});
* End - Controls action based on clicked Text only
*/
/*
* Controls action based on clicked checkbox
*/
cBox.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
EventDbAdapter mDbHelper = new EventDbAdapter(context);
mDbHelper.open();
CheckBox cBox = (CheckBox) v.findViewById(R.id.bcheck);
if (cBox.isChecked()) {
//cBox.setChecked(false);
CharSequence charseq = "Added to My Schedule";
Toast.makeText(context, charseq, Toast.LENGTH_SHORT).show();
// Update the database for each checked item
mDbHelper.updateChecked(cBox.getTag().toString(), "1");
c.requery();
// Verify that the db was updated for debugging purposes
String event = c.getString(c.getColumnIndex("event"));
int id = (Integer) cBox.getTag();
Log.i("checked _id...........", "id= " + id + " " +c.getString(c.getColumnIndex("_id")));
Log.i("checked checked...........", ""+c.getString(c.getColumnIndex("checked")));
} else if (!cBox.isChecked()) {
//cBox.setChecked(true);
CharSequence charseq = "Removed from My Schedule";
Toast.makeText(context, charseq, Toast.LENGTH_SHORT).show();
// checkList.remove(cBox.getTag());
//checkList.add((Integer) cBox.getTag());
String event = c.getString(c.getColumnIndex("event"));
//int id = c.getInt(c.getColumnIndex("_id"));
int id = (Integer) cBox.getTag();
mDbHelper.updateChecked(cBox.getTag().toString(), "0");
c.requery();
//int sqlresult = mDbHelper.selectChk(id, event);
//Log.i("sqlresult checked value after update...........", ""+ sqlresult);
//Log.i("unchecked _id...........", ""+c.getString(c.getColumnIndex("_id")));
//Log.i("unchecked checked...........", ""+c.getString(c.getColumnIndex("checked")));
}
//mDbHelper.close();
}
});
return(v);
}
}

Related

Maintaining checkbox states in listview with CursorAdapter

For my Android project, I have a listview which has a checkbox for every item. The data is loaded from an SQLite database by using a CursorAdapter class. However, whenever I scroll, the checkbox positions will get moved and get carried down to the next part of the listview. How can I fix this problem?
GIF of my CheckBox Problem
Here's my Cursor Adapter Class:
public class VocabCursorAdapter extends CursorAdapter {
private static final int DIFFICULT = 0;
private static final int FAMILIAR = 1;
private static final int EASY = 2;
private static final int PERFECT = 3;
public VocabCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.item_vocab, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Find fields to populate in inflated template
TextView tvVocabName = (TextView) view.findViewById(R.id.vocabName);
TextView tvVocabDefinition = (TextView) view.findViewById(R.id.vocabDefinition);
ImageView tvVocabLevel = (ImageView) view.findViewById(R.id.vocabLevel);
// Extract properties from cursor
String vocab = cursor.getString(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_VOCAB));
String definition = cursor.getString(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_DEFINITION));
int level = cursor.getInt(cursor.getColumnIndexOrThrow(VocabDbContract.COLUMN_NAME_LEVEL));
// Populate fields with extracted properties
tvVocabName.setText(vocab);
tvVocabDefinition.setText(definition);
if (level == DIFFICULT) {
tvVocabLevel.setImageResource(R.drawable.level_bars_difficult);
tvVocabLevel.setTag(DIFFICULT);
}
else if (level == FAMILIAR) {
tvVocabLevel.setImageResource(R.drawable.level_bars_familiar);
tvVocabLevel.setTag(FAMILIAR);
}
else if (level == EASY) {
tvVocabLevel.setImageResource(R.drawable.level_bars_easy);
tvVocabLevel.setTag(EASY);
}
else if (level == PERFECT) {
tvVocabLevel.setImageResource(R.drawable.level_bars_perfect);
tvVocabLevel.setTag(PERFECT);
}
}
And here's my list item xml, item_vocab.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="wrap_content"
android:longClickable="true">
<ImageView
android:layout_width="36sp"
android:layout_height="36sp"
android:id="#+id/vocabLevel"
android:layout_gravity="right"
android:src="#drawable/level_bars"
android:scaleType="fitXY"
android:contentDescription="#string/vocab_level"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/editCheckbox"
android:layout_toStartOf="#+id/editCheckbox"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="#+id/vocabName"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="#+id/vocabLevel"
android:layout_toStartOf="#+id/vocabLevel"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Small Text"
android:id="#+id/vocabDefinition"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="#+id/vocabLevel"
android:layout_toStartOf="#+id/vocabLevel"
android:layout_below="#id/vocabName"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/editCheckbox"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
And here's my xml which contains a listview
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".controller.MyVocab"
android:paddingLeft="5dp">
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/mVocabList"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/empty_text_view"
android:id="#android:id/empty"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
</RelativeLayout>
I have looked at a lot of different solutions on StackOverflow, but I wasn't able to successfully do it in my own app. For an example, this post has a similar problem, but its solution used getView and I had trouble understanding how to implement it with newView and bindView instead.
And some other solutions might be examples where a cursoradapter is not involved. Any help is much appreciated, thanks a lot!
Edit #1: After incorporating Phan's changes, the checkbox states get resets to false rather than keeping its states when I scroll the listview (See ).
Reason : ListView re-uses the views.
Solution :
class VocabCursorAdapter extends CursorAdapter {
List<Integer> selectedItemsPositions;//to store all selected items position
public VocabCursorAdapter(Context context, Cursor c,int flags) {
super(context, c,0);
selectedItemsPositions = new ArrayList<>();
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
View view = LayoutInflater.from(context).inflate(R.layout.item_vocab, viewGroup, false);
CheckBox box = (CheckBox) view.findViewById(R.id.editCheckbox);
box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
int position = (int) compoundButton.getTag();
if (b) {
//check whether its already selected or not
if (!selectedItemsPositions.contains(position))
selectedItemsPositions.add(position);
} else {
//remove position if unchecked checked item
selectedItemsPositions.remove((Object) position);
}
}
});
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
//your other stuff
CheckBox box = (CheckBox) view.findViewById(R.id.editCheckbox);
box.setTag(cursor.getPosition());
if (selectedItemsPositions.contains(cursor.getPosition()))
box.setChecked(true);
else
box.setChecked(false);
}
}
Try this
public class VocabCursorAdapter extends CursorAdapter {
private ArrayList<Boolean> itemChecked = new ArrayList<Boolean>(); // array list for store state of each checkbox
public VocabCursorAdapter(Context context, Cursor c, int flags) {
for (int i = 0; i < c.getCount(); i++) { // c.getCount() return total number of your Cursor
itemChecked.add(i, false); // initializes all items value with false
}
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
...
final int position = cursor.getPosition(); // get position by cursor
CheckBox checkBox = (CheckBox) view.findViewById(R.id.editCheckbox);
checkBox.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (itemChecked.get(position) == true) { // if current checkbox is checked, when you click -> change it to false
itemChecked.set(position, false);
} else {
itemChecked.set(position, true);
}
}
});
checkBox.setChecked(itemChecked.get(position)); // set the checkbox state base on arraylist object state
Log.i("In VocabCursorAdapter","position: "+position+" - checkbox state: "+itemChecked.get(position));
}
}
public class ObservationselectattributeFragment extends Fragment {
DatabaseHandler mDBHandler;
ListView mListView;
SimpleCursorAdapter mSCA;
Cursor mCsr;
ArrayList<String> attributeItems = new ArrayList<>();
public ObservationselectattributeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate the layout for this fragment
View view1=inflater.inflate(R.layout.fragment_observationselectattribute, container, false);
//Bundle bundle2 = getArguments();
Bundle bundle1 = getArguments();
final int firsttext= bundle1.getInt("TotalCount");
final String selectedtreatment= bundle1.getString("SelectedTreatment");
Toast.makeText(getActivity(),"value \n"+firsttext+"\n"+"treatment \n"+selectedtreatment, Toast.LENGTH_SHORT).show();
// Toast.makeText(getActivity(),"SelectedTreatment \n"+selectedtreatment, Toast.LENGTH_SHORT).show();
mListView = (ListView)view1.findViewById(R.id.lv001);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
Button addattribute = (Button)view1.findViewById(R.id.addattribute);
addattribute.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String items1="";
Integer tcount1=0;
for(String item1:attributeItems){
items1+="-"+item1+"\n";
tcount1++;
}
Toast.makeText(getActivity(),"you have selected \n"+items1,Toast.LENGTH_LONG).show();
Toast.makeText(getActivity(),"you have selected \n"+tcount1,Toast.LENGTH_LONG).show();
/*FragmentTransaction fr= getFragmentManager().beginTransaction();
fr.replace(R.id.main_container, new ShowObservationDataRecordingFragment()).addToBackStack("ObservationselectattributeFragment");
fr.commit();*/
Bundle bundle = new Bundle();
bundle.putInt("TotalCount2",firsttext);
bundle.putInt("TotalCount1", tcount1);
bundle.putString("SelectedTreatment", selectedtreatment);
Fragment showobservationdatarecordingfragment = new ShowObservationDataRecordingFragment();
showobservationdatarecordingfragment.setArguments(bundle);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.main_container, showobservationdatarecordingfragment).addToBackStack("ObservationselectattributeFragment").commit();
}
});
mDBHandler = new DatabaseHandler(this.getActivity());
mCsr = mDBHandler.getAllRecords();
// Prepare a list of the columns to get the data from, for the ListViewt
String[] columns_to_get_data_from = new String[]{
DatabaseHandler.KEY_IDS,
DatabaseHandler.KEY_NAMES,
DatabaseHandler.KEY_FNAME,
DatabaseHandler.KEY_MONAME,
DatabaseHandler.KEY_SNAME
};
// Prepare a list of the Views into which to place the data
int[] itemviews_to_place_data_in = new int[]{
R.id.euserid,
R.id.eusername,
R.id.efname,
R.id.emoname,
R.id.esname
};
// get and instance of SimpleCursorAdapter
mSCA = new SimpleCursorAdapter(getActivity(),
R.layout.listviewitem_record,
mCsr,
columns_to_get_data_from,
itemviews_to_place_data_in,
0);
// Save the ListView state (= includes scroll position) as a Parceble
Parcelable state = mListView.onSaveInstanceState();
// get and instance of SimpleCursorAdapter the listviewitem_record layout
mListView.setAdapter(mSCA);
// Restore previous state (including selected item index and scroll position)
mListView.onRestoreInstanceState(state);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String attributeItem1 = ((TextView)view.findViewById(R.id.euserid)).getText().toString();
String attributeItem2 = ((TextView)view.findViewById(R.id.eusername)).getText().toString();
String attributeItem3 = ((TextView)view.findViewById(R.id.efname)).getText().toString();
String attributeItem4 = ((TextView)view.findViewById(R.id.emoname)).getText().toString();
String attributeItem5 = ((TextView)view.findViewById(R.id.esname)).getText().toString();
String attributeItem = attributeItem1 + attributeItem2 + attributeItem3 + attributeItem4 + attributeItem5;
// CheckedTextView box = (CheckedTextView) view.findViewById(R.id.record_checkbox);
// box.setChecked(true);
CheckedTextView checkedTextView = (CheckedTextView) view.findViewById(R.id.record_checkbox);
if(checkedTextView.isChecked()) {
checkedTextView.setChecked(false);
} else {
checkedTextView.setChecked(true);
}
if(attributeItems.contains(attributeItem)){
attributeItems.remove(attributeItem);//uncheck item
}
else
{
attributeItems.add(attributeItem);
}
Toast.makeText(getActivity(), "Item1 = " + attributeItem1 +"\n"+ "Item2 ="+attributeItem2 +"\n"+"Item3 ="+attributeItem3+"\n"+"Item4 ="+attributeItem4+"\n"+"Item5 ="+attributeItem5, Toast.LENGTH_SHORT).show();
}
});
((HomeActivity) getActivity())
.setActionBarTitle("Select Attribute");
return view1;
}
}

ListView not showing data from custom Adapter

My getView() method for customized ListViewAdapter is as follows :
public class ListViewAdapter extends BaseAdapter {
Context mContext;
LayoutInflater mInflater;
ArrayList mArray;
ArrayList<Item> mArray2;
DBHelper mydb;
String dbName;
public ListViewAdapter(Context context, LayoutInflater inflater) {
mContext = context;
mInflater = inflater;
mArray = new ArrayList();
mArray2 = new ArrayList<>();
mydb = new DBHelper(mContext);
}
#Override
public int getCount() {
return mArray.size();
}
#Override
public Object getItem(int position) {
return mArray.get(position);
}
public Item getItem2(int position) { return mArray2.get(position); }
#Override
public long getItemId(int position) {
// your particular data set uses String IDs
// but you have to put something in this method
return position;
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
ViewHolder holder;
// check if the view already exists
// if so, no need to inflate and findViewById again!
if (convertView == null) {
// Inflate the custom row layout from your XML.
convertView = mInflater.inflate(R.layout.list_item, null);
// create a new "Holder" with subviews
holder = new ViewHolder();
holder.itemNameView = (TextView) convertView.findViewById(R.id.item_name);
holder.itemExpiryView = (TextView) convertView.findViewById(R.id.item_expiry);
// Taking care of the buttons
holder.editButton = (Button) convertView.findViewById(R.id.button_edit);
holder.deleteButton = (Button) convertView.findViewById(R.id.button_delete);
// hang onto this holder for future recycling
convertView.setTag(holder);
} else {
// skip all the expensive inflation/findViewById
// and just get the holder you already made
holder = (ViewHolder) convertView.getTag();
}
// Set listener on the buttons
holder.editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, "Edit Button CLicked", Toast.LENGTH_SHORT).show();
}
});
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String name = getItem(position).toString();
int id = mydb.getID(dbName, name);
mydb.deleteItem(dbName, id);
mArray2 = mydb.getAllItemsAsCollection(dbName);
notifyDataSetChanged();
Toast.makeText(mContext, "Item deleted", Toast.LENGTH_SHORT).show();
}
});
// Doing for 2nd case
Item _item = getItem2(position);
String name2 = _item.name;
System.out.println(name2);
String ex = _item.expiry;
System.out.println(ex);
// For the second case
holder.itemNameView.setText(name2);
holder.itemExpiryView.setText(ex);
return convertView;
}
// this is used so you only ever have to do
// inflation and finding by ID once ever per View
private static class ViewHolder {
public TextView itemNameView;
public TextView itemExpiryView;
public Button editButton;
public Button deleteButton;
}
public void updateData2(ArrayList<Item> arrayPassed) {
// update the adapter's data set
mArray2 = arrayPassed;
notifyDataSetChanged();
}
public void setDbName(String dbName){
this.dbName = dbName;
}
}
The DBHelper class function getAllItemsAsCollection() is defined as below :
public ArrayList<Item> getAllItemsAsCollection(String dbName)
{
ArrayList<Item> array_list = new ArrayList<Item>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery( "select * from " + dbName, null );
res.moveToFirst();
while(res.isAfterLast() == false){
String n = res.getString(res.getColumnIndex(COLUMN_NAME));
String e = res.getString(res.getColumnIndex(COLUMN_EXPIRY));
String c = dbName;
Item _item = new Item(n, e, c);
array_list.add(_item);
res.moveToNext();
}
return array_list;
}
And also, the insertItem() function inside DBHelper is this :
public boolean insertItem (String dbName, String name, String expiry)
{
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("name", name);
contentValues.put("expiry", expiry);
db.insert(dbName, null, contentValues);
return true;
}
I have added a separate class for customizable object handing :
public class Item {
String name;
String expiry;
String category;
Item(String n, String e, String c){
this.name = n;
this.expiry = e;
this.category = c;
}
}
And the addItem() method inside MainActivity.java works like :
public void addItem(final View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this);
LinearLayout lila1 = new LinearLayout(this);
lila1.setOrientation(LinearLayout.VERTICAL);
final EditText name = new EditText(this);
name.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
final EditText days = new EditText(this);
days.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
TextView text_ex = new TextView(this);
text_ex.setText("In how many days will it expire..");
alert.setTitle("Hello!");
alert.setMessage("What did you buy today..");
lila1.addView(name);
lila1.addView(text_ex);
lila1.addView(days);
alert.setView(lila1);
// Make an "OK" button to save the name
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Grab the EditText's input
String inputName = name.getText().toString();
String daysToExpiry = days.getText().toString();
System.out.println(daysToExpiry);
mydb.insertItem(currentDB, inputName, daysToExpiry);
System.out.println("Worked");
// For 2nd Case
currentList2 = mydb.getAllItemsAsCollection(currentDB);
System.out.println("Random Musings");
itemAdder2.updateData2(currentList2);
// addItemToList(inputName, v);
dialog.dismiss();
}
});
// Make a "Cancel" button
// that simply dismisses the alert
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
alert.show();
}
I have initiaized every variable correctly. When I try to run my app, and try to add a new item, the Dialog box just vanishes and there is nothing shown in the layout. I tried with a simple ArrayList<Strings> before and it worked perfectly. That is why I believe there should not be any problem with the .xml Layout. And might be with the ListViewAdapter.updateData2() function. Please Help. Appreciate your patience going through these long pieces of code. If any further info is required, please let me know. Thanks a lot. :)
Forgot to attach the .xml for actual view. This worked perfectly with ArrayList<String>. I have already tested. When I tried to pass complex object, in this case, Item-class object, and correspondingly an ArrayList<Item>, my guess is, I could not write the adapter portion code correctly.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<TextView
android:id="#+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/item_expiry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/item_name" />
<Button
android:id="#+id/button_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/item_expiry"
android:layout_alignParentLeft="true"
android:text="Edit"
android:clickable="true" />
<Button
android:id="#+id/button_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/item_expiry"
android:layout_alignParentRight="true"
android:text="Delete"
android:clickable="true" />
</RelativeLayout>

Custom SimpleCursorAdapter doesn't bind data, and shows rows even when there's no data

So I'm trying to make a custom SimpleCursorAdapter, because I want to make list rows that look something like this:
ToggleButton | TextView | ImageButton,
and I know of no way to do this without making a custom adapter.
The problem being that my code doesn't work and I'm not really sure why. Even if there's no data to be displayed, I get a row with the default format:
ToggleButton | "default" | ImageButton.
Furthermore, all rows displayed look exactly the same as the default row, and the OnClickListener I set up doesn't do anything.
Can someone tell me what I'm doing wrong, or at least point me in the direction of a decent tutorial for how to deal with custom CursorAdapters and OnClickListeners? Because I've been totally unable to find anything remotely helpful.
Here is my code for the adapter:
public class AlarmCursorAdapter extends SimpleCursorAdapter {
private Cursor mCursor;
private Context mContext;
private Activity mActivity;
public AlarmCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
// TODO Auto-generated constructor stub
mCursor = c;
mContext = context;
mActivity = (Activity) context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.alarm_list_row, parent, false);
}
if(mCursor == null || mCursor.getCount() == 0) {
return view;
}
mCursor.moveToPosition(position);
// Set the alarm time view
TextView alarmView = (TextView) view.findViewById(R.id.alarmView);
int timeStringIndex = mCursor.getColumnIndexOrThrow(DailyAlarmTable.ALARM_TIME);
String alarmTime = mCursor.getString(timeStringIndex);
alarmView.setText(alarmTime);
// Set up the toggle button
int isActiveIndex = mCursor.getColumnIndexOrThrow(DailyAlarmTable.ALARM_ISACTIVE);
int isActive = mCursor.getInt(isActiveIndex);
ToggleButton alarmToggle = (ToggleButton)view.findViewById(R.id.alarmToggle);
if(isActive == 1) {
alarmToggle.setChecked(true);
} else {
alarmToggle.setChecked(false);
}
final int currentPosition = mCursor.getPosition();
int idIndex = mCursor.getColumnIndexOrThrow(DailyAlarmTable.ALARM_ID);
final long alarmId = mCursor.getLong(idIndex);
alarmToggle.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
String toastStr = "clicked alarm " + alarmId + " at position " + currentPosition;
Toast.makeText(mContext, toastStr, Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
Here's the implementation, which occurs inside a fragment:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
/*
mAdapter = new SimpleCursorAdapter(getActivity(),
R.layout.alarm_list_row, null,
new String[] { DailyAlarmTable.ALARM_TIME, DailyAlarmTable.ALARM_ISACTIVE },
new int[] { R.id.alarmView, R.id.alarmToggle }, 0);
*/
mAdapter = new AlarmCursorAdapter(getActivity(),
R.layout.alarm_list_row, null,
new String[] { DailyAlarmTable.ALARM_TIME, DailyAlarmTable.ALARM_ISACTIVE },
new int[] { R.id.alarmView, R.id.alarmToggle }, 0);
//mAdapter.setViewBinder(new AlarmViewBinder());
ListView alarmList = (ListView) this.getActivity().findViewById(R.id.alarmListView);
TextView emptyView = (TextView) this.getActivity().findViewById(R.id.empty);
alarmList.setEmptyView(emptyView);
alarmList.setAdapter(mAdapter);
// Initialize the loader
getLoaderManager().initLoader(1, savedInstanceState, this);
}
Here's the XML file for the row:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ToggleButton
android:id="#+id/alarmToggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="#string/details_default" />
<TextView
android:id="#+id/alarmView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/details_default"
android:textAppearance="?android:attr/textAppearanceMedium" />
<ImageButton
android:id="#+id/alarmDiscard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_discard"
android:contentDescription="#string/alarm_discard_description" />
</LinearLayout>
If there's any other code you need, I can gladly add that. Thank you very much in advance.
As suggested by pskink's comment, the solution was not to use a custom SCA at all, but to just implement a View Binder.

Items selected in a list are not shown as selected

Now this might be a difficult to understand problem.
What I actually wanted : To get a populated list of distinct callers from call log, let user select as manny he wants and then blacklist them by tapping on a button.
Thus I concluded that what I wanted is following
Concluded What I wanted : To have an activity with a header button and a list of items. From the list of items, user can select any number of the items(implemented this by using checkboxes, will show how I did it later) and then perform an action by selecting header button.
What I did : The activity layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >
<LinearLayout android:id="#+id/add_btn_layout" android:layout_width="match_parent" android:layout_height="50dp" android:padding="2dp" >
<Button android:id="#+id/btn_blacklist_sender" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="21dp" >
</Button>
</LinearLayout>
<ListView android:id="#+id/list_chose_to_blacklist" android:layout_width="wrap_content" android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
The row layout :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:padding="2dp" >
<CheckBox android:id="#+id/isDelete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="43dp" android:paddingRight="3dp" />
</LinearLayout>
In the activity:
public class CallerChooseToBlacklistActivity extends Activity {
Button btnBlacklistCaller;
ListView listCallers;
HashSet<String> toBlacklistNumbers = new HashSet<String>();
String caller = "Sender";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chose_to_blacklist);
btnBlacklistCaller = (Button) findViewById(R.id.btn_blacklist_sender);
btnBlacklistCaller.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
BlacklistNumberHelper helper = new BlacklistNumberHelper(
getApplicationContext());
for (String number : toBlacklistNumbers) {
helper.insert(number, DbHelper.TABLE_CALL_BLACKLIST,
DbHelper.C_CALL_BLACKLIST_NO);
}
helper.close();
onResume();
}
});
btnBlacklistCaller.setText(getString(R.string.btn_blacklist_caller));
listCallers = (ListView) findViewById(R.id.list_chose_to_blacklist);
}
#Override
protected void onResume() {
super.onResume();
String[] projection = new String[] { CallLog.Calls._ID,
CallLog.Calls.NUMBER };
Cursor cursor = getContentResolver().query(CallLog.Calls.CONTENT_URI,
projection, null, null, null);
for (int idx = 0; idx < cursor.getColumnCount(); idx++) {
Log.d(MainActivity.TAG, idx + ":" + cursor.getColumnName(idx));
}
HashSet<String> distinctCallers = new HashSet<String>();
ArrayList<String> allreadyBlacklisted = (new BlacklistNumberHelper(
getApplicationContext())).getAllNumbers(
DbHelper.TABLE_CALL_BLACKLIST, DbHelper.C_CALL_BLACKLIST_NO);
if (cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) {
try {
String address = cursor.getString(
cursor.getColumnIndexOrThrow("number")).toString();
boolean isPresent = false;
for (String no : allreadyBlacklisted)
if (no.equalsIgnoreCase(address)) {
isPresent = true;
break;
}
if (!isPresent)
distinctCallers.add(address);
cursor.moveToNext();
} catch (IllegalArgumentException e) {
}
}
}
ArrayList<HashMap<String, String>> fillMaps = new ArrayList<HashMap<String, String>>();
for (String address : distinctCallers) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(caller, address);
fillMaps.add(map);
}
String[] from = { caller };
int[] to = { R.id.isDelete };
SimpleAdapter cursorAdapter = new SimpleAdapter(this, fillMaps,
R.layout.row_blacklist, from, to);
cursorAdapter.setViewBinder(VIEW_BINDER);
listCallers.setAdapter(cursorAdapter);
}
// custom binder to bind columns customally
final ViewBinder VIEW_BINDER = new ViewBinder() {
public boolean setViewValue(View view, Object arg1, String arg2) {
if (view.getId() == R.id.isDelete) {
CheckBox cb = (CheckBox) view;
cb.setText(arg2);
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
String text = buttonView.getText().toString();
if (isChecked)
toBlacklistNumbers.add(text);
else if (toBlacklistNumbers.contains(text))
toBlacklistNumbers.remove(text);
}
});
return true;
} else {
return false;
}
}
};
}
What the problem is : When I select an item and scroll down and come back by scrolling up the selected item check on the selected item gets changed. Although when I look at the hashset toBlacklistNumbers the number I originally selected is there only. That means when I tap on the header button it does blacklist it. To remove it from the hashset I have to select it first, then unselect it. But this is not what I want.
I dont want the item to get unselected when I scroll down the list. This is obviously not the problem with the android version as I have checked it on Android 2.2 and Android 4.1
Update : By selected item getting changed, I meant that the item I selected is not selected any more, instead any item above it or below it is selected. Also, When I scroll down, many items in the list below are also selected automatically
You aren't handling the recycling right, you aren't setting the status of the CheckBox based on the data in toBlacklistNumbers:
#Override
public boolean setViewValue(View view, Object arg1, String arg2) {
if (view.getId() == R.id.isDelete) {
CheckBox cb = (CheckBox) view;
cb.setText(arg2);
String data = (String) arg1;
if (toBlacklistNumbers.contains(data)) {
cb.setChecked(true);
} else {
cb.setChecked(false);
}
cb.setOnCheckedChangeListener(mListener);
return true;
} else {
return false;
}
}
where mListener is a single OnCheckedChangeListener so you don't create each time one when the user scrolls:
private OnCheckedChangeListener mListener = new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
String text = buttonView.getText().toString();
if (isChecked)
toBlacklistNumbers.add(text);
else if (toBlacklistNumbers.contains(text))
toBlacklistNumbers.remove(text);
}
};
Also, don't call the onResume method yourself, that is a lifecycle method to be called only by the system.

How to print data in a ListView using ListAdapter

Updated
I'm trying to print the data retrieved from the database on a list view. For a while my application print for each data in the database, a row on the list view. So if there are 10 data in the database, the app prints 10 rows, corresponding to each row of the database. Here is a view of how it is being printed.
The new image of how it looks now:
But, it is not printing the data as I want. I want to print a column of the row in a specific text view, but it doesnt show anything.
So the activity RatedCalss calls the method selectTopCalls() and makes a List receive the list that this method returns. And then I pass this list to the listAdapter.
Well I have this activity named RatedCalls.java:
public class RatedCalls extends Activity {
private static final String LOG_TAG = "RatedCalls";
private CallDataHelper cdh;
private ListView listview;
private ArrayList<String> ratedCallsList;
private MyListAdapter listAdapter;
private View view;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i(LOG_TAG, "calling from onCreate()");
cdh = new CallDataHelper(this);
startService(new Intent(this, RatedCallsService.class));
setBasicContent();
}
public void setBasicContent() {
listview = (ListView) findViewById(R.id.list_view);
ratedCallsList = this.cdh.selectTopCalls();
Log.i(LOG_TAG, "ratedCallsList size: " + ratedCallsList.size());
listAdapter = new MyListAdapter(this, this, R.id.list_view, ratedCallsList);
listview.setAdapter(listAdapter);
}
}
I have this class, a ListAdapter class named MyListAdapter.java:
public class MyListAdapter extends ArrayAdapter { //--CloneChangeRequired
private ArrayList mList;
private Context mContext;
private Activity mActivity;
private int selectedPos = -1; // init value for not-selected
private ArrayList<String> ratedCallsList;
private CallDataHelper cdh;
public void setSelectedPosition(int pos){
selectedPos = pos;
notifyDataSetChanged();
}
public int getSelectedPosition(){
return selectedPos;
}
public MyListAdapter(Context context, Activity activity, int textViewResourceId, ArrayList list) {
super(context, textViewResourceId, list);
this.mList = list;
this.mContext = context;
this.mActivity = activity;
}
public View getView(int position, View convertView, ViewGroup parent){
View view = convertView;
try{
if (view == null) {
LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.list_item, null); // --CloneChangeRequired(list_item)
}
// setting STRIP BG
if(position == selectedPos){
view.findViewById(R.id.rlt_main).setBackgroundColor( Color.rgb(062, 076, 120) );
}else if(position%2==0){
view.findViewById(R.id.rlt_main).setBackgroundColor( Color.rgb(226, 231, 239) );
}else{
view.findViewById(R.id.rlt_main).setBackgroundColor( Color.rgb(200, 210, 223) );
}
setViews(position, view);
}catch(Exception e){
//Log.i(MyListAdapter.class.toString(), e.getMessage());
}
return view;
}
public void setViews(int position, View view) {
cdh = new CallDataHelper(mContext);
if(mContext.getClass().equals((RatedCalls.class))){
ratedCallsList = this.cdh.selectTopCalls();
Log.i("MYLISTADAPTER", "size " + ratedCallsList.size());
if (ratedCallsList != null) {
((TextView) view.findViewById(R.id.contact_name)).setText(ratedCallsList.get(0));
((TextView) view.findViewById(R.id.phone_number)).setText(ratedCallsList.get(1));
((TextView) view.findViewById(R.id.duration)).setText(ratedCallsList.get(2));
((TextView) view.findViewById(R.id.date)).setText(ratedCallsList.get(3));
}
}else if(mContext.getClass().equals(RatedContacts.class)){
final PublishersBO listPublisher = (PublishersBO) mList.get(position);
if (listPublisher != null) {
//--setting list_item views
((TextView) view.findViewById(R.id.contact_name)).setText(listPublisher.getName());
((TextView) view.findViewById(R.id.phone_number)).setText(listPublisher.getEmail());
//--onClickListener
view.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
Intent myIntent = new Intent(mContext,RatedContacts.class);
myIntent.putExtra("NAME", listPublisher.getName());
myIntent.putExtra("ACTIVITY_NAME", mContext.getClass().toString());
mContext.startActivity(myIntent);
mActivity.finish();
}
});
}
}
}
}
The method that retrieve the data from the database is in a separated class that deals with SQLite function. This is the method:
public ArrayList<String> selectTopCalls() {
ArrayList<String> list1 = new ArrayList<String>();
Cursor cursor = this.db.query(TABLE_NAME, null, null, null, null, null,
"duration desc");
if (cursor.moveToFirst()) {
do {
//if (cursor.getString(2) != "") {
cdObj = new CallData();
list1.add(cursor.getString(2));
list1.add(cursor.getString(4));
list1.add(cursor.getString(5));
list1.add(cursor.getString(6));
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return list1;
}
And here is the xml file for the view named list_item.xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="67px"
android:id="#+id/rlt_main"
android:layout_toRightOf="#+id/iv_forward">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:id="#+id/iv_forward"
android:src="#drawable/icon"
android:layout_alignParentLeft="true">
</ImageView>
<TextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="#+id/phone_number"
android:layout_alignParentBottom="true"
android:layout_toRightOf="#+id/iv_forward"
android:layout_alignBottom="#+id/iv_forward">
</TextView>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/duration"
android:layout_alignBottom="#+id/phone_number"
android:layout_alignRight="#+id/phone_number"
>
</TextView>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/date"
android:layout_alignBottom="#+id/contact_name"
android:layout_alignRight="#+id/contact_name"
>
</TextView>
<TextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="#+id/contact_name"
android:textSize="20px"
android:layout_toRightOf="#+id/iv_forward"
android:layout_alignParentTop="true">
</TextView>
So I'd like to print the data in the text views in the activity, but I dont know where to set the text, in what class, if in the MyListAdapter class or if in the activity.
Thanks.
I have made few changes to your code, check here,
https://gist.github.com/683b84af9d01bf18fe3d
Thanks.........

Categories

Resources