I'm working on an app that allows the user to create notes while rehearsing a play. The user can view the notes they have created in a listview, and edit and delete them if they wish.
Take for example the user creates 3 notes. In the database, the row_id's will be 1, 2 and 3. So when the user views the notes in the listview, they will also be in the order 1, 2, 3 (intially 0, 1, 2 before I increment the values). So the user can view and delete the correct row from the database.
The problem arises when the user decides to delete a note. Say the user deletes the note in position 2. Thus our database will have row_id's 1 and 3. But in the listview, they will be in the position 1 and 2. So if the user clicks on the note in position 2 in the listview it should return the row in the database with row_id 3. However it tries to look for the row_id 2 which doesn't exist, and hence crashes.
I need to know how to obtain the corresponding row_id, given the user's selection in the listview. Here is the code below that does this:
// When the user selects "Delete" in context menu
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
switch (item.getItemId()) {
case DELETE_ID:
deleteNote(info.id + 1);
return true;
}
return super.onContextItemSelected(item);
}
// This method actually deletes the selected note
private void deleteNote(long id) {
Log.d(TAG, "Deleting row: " + id);
mNDbAdapter.deleteNote(id);
mCursor = mNDbAdapter.fetchAllNotes();
startManagingCursor(mCursor);
fillData();
// TODO: Update play database if there are no notes left for a line.
}
// When the user clicks on an item, display the selected note
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
viewNote(id, "", "", true);
}
// This is where we display the note in a custom alert dialog. I've ommited
// the rest of the code in this method because the problem lies in this line:
// "mCursor = mNDbAdapter.fetchNote(newId);"
// I need to replace "newId" with the row_id in the database.
private void viewNote(long id, String defaultTitle, String defaultNote,
boolean fresh) {
final int lineNumber;
String title;
String note;
id++;
final long newId = id;
Log.d(TAG, "Returning row: " + newId);
mCursor = mNDbAdapter.fetchNote(newId);
lineNumber = (mCursor.getInt(mCursor.getColumnIndex("number")));
title = (mCursor.getString(mCursor.getColumnIndex("title")));
note = (mCursor.getString(mCursor.getColumnIndex("note")));
.
.
.
}
Let me know if you would like me to show anymore code. It seems like something so simple but I just can't find a solution.
Thanks!
EDIT
I fixed the problem. I answered the question below.
Okay I fixed the problem (temporarily at least).
The problem was I was using my own adapter class I created. It was not keeping track of which item in the list corresponded with the item in the database. I have now rewritten the "fillData()" method using a SimpleCursorAdapter.
This is the OLD implementation, with my own custom adapter:
private void fillData() {
String note;
notes = new ArrayList<String>();
// Populate arraylist with database data
if (mCursor.moveToFirst()) {
do {
// Get current row's character and line
note = mCursor.getString(mCursor.getColumnIndex("title"));
Log.d(TAG, "Adding note: " + note);
notes.add(note);
} while (mCursor.moveToNext());
}
// Fill list with our custom adapter
NoteAdapter adapter = new NoteAdapter(this, R.layout.note_row_layout,
notes);
setListAdapter(adapter);
}
and this is the same class rewitten with a SimpleCursorAdapter:
private void fillData() {
mFrom = new String[] { NoteDbAdapter.KEY_TITLE };
mTo = new int[] { R.id.textNote };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.note_row_layout, mCursor, mFrom, mTo);
setListAdapter(adapter);
}
The reason I created my own adapter class was because I also had a checkbox in each row of the list (for multi-delete).
My question now is, am I able to obtain the state of each checkbox (whether its checked or not) when the user decides to do a multiple delete when using a SimpleCursorAdapter?
Thanks.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I am opening a new activity which uses CursorLoader to get data from local SQLite db. Whenever I click the listitem, for some reason it always populates the values from the first item. I want to populate the values for the specific list item I clicked. For eg: If I click "watch" item, it still populates "book" values in Edittext. Am I missing anything here?
Here is my code for CursorLoader:
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
String[] projection = {
ItemContract.ItemEntry._ID,
ItemContract.ItemEntry.COLUMN_ITEM_NAME,
ItemContract.ItemEntry.COLUMN_ITEM_QUANTITY,
ItemContract.ItemEntry.COLUMN_ITEM_PRICE,
ItemContract.ItemEntry.COLUMN_ITEM_IMAGE
};
return new CursorLoader(this, ItemContract.ItemEntry.CONTENT_URI,projection,null,null,null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor == null || cursor.getCount() < 1) {
return;
}
if (cursor.moveToFirst()) {
int nameColumnIndex = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_ITEM_NAME);
int quantityColumnIndex = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_ITEM_QUANTITY);
int priceColumnIndex = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_ITEM_PRICE);
int imageColumnIndex = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_ITEM_IMAGE);
String editCurrentName = cursor.getString(nameColumnIndex);
int editCurrentQuantity = cursor.getInt(quantityColumnIndex);
Float editCurrentPrice = cursor.getFloat(priceColumnIndex);
String editCurrentImage = cursor.getString(imageColumnIndex);
if(editCurrentImage != null) {
editImage.setVisibility(View.VISIBLE);
editImage.setImageURI(Uri.parse(editCurrentImage));
}
else {
editImage.setVisibility(View.GONE);
}
editName.setText(editCurrentName);
editQuantity.setText(Integer.toString(editCurrentQuantity));
editPrice.setText(Float.toString(editCurrentPrice));
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.e("EDITACTIVITY", "onLoaderReset: ");
editName.setText("");
editQuantity.setText("");
editPrice.setText("");
editImage.setImageDrawable(null);
}
This will be an issue with how you retrieve the Cursorfrom the ListView or AnyView's position. The way to retrieve values from the Cursor you got from the onItemClickListener in the ListView or AnyView's itself:
public void onItemClick(AdapterView<?> listView, View view, int position, long id) {
//Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Use the Cursor to retrieve the value from here!
}
You are simple loading cursor, so without passing condition it will return all data and then also you are fetching first row data. So it will only return very first data in all scenarios.
Solution
In this case there are three solutions available which you can use.
Solution1:
When user click on item, at that time you can fetch the item object and pass the values to edit activity.
Something like this: [http://www.viralandroid.com/2016/04/start-new-activity-from-android-listview-onitem-clicked.html][1]
Solution2:
When user click on item, at that time fetch only the item id and pass to the edit activity. Now you have to pass argument as below in cursor loader.
// Here I am storing passed id in one variable, you have to set selected_item_id in intent while starting new activity
int selectedId = getIntent().getExtras().getInt("selected_item_id",0);
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
String[] projection = {
ItemContract.ItemEntry._ID,
ItemContract.ItemEntry.COLUMN_ITEM_NAME,
ItemContract.ItemEntry.COLUMN_ITEM_QUANTITY,
ItemContract.ItemEntry.COLUMN_ITEM_PRICE,
ItemContract.ItemEntry.COLUMN_ITEM_IMAGE
};
String selection = ItemContract.ItemEntry._ID + "=?";
String[] selectionArgs = { String.valueOf(selectedId) };
return new CursorLoader(this, ItemContract.ItemEntry.CONTENT_URI,projection, selection ,selectionArgs ,null);
}
Now, you will get actual result.
In third solution you have to use your own method for loader and then in finished you have to fetch one by one cursor data and then match with selectedId.
Last one is little bit complex and lengthy so try to use first or second only.
Currently, in anotherclass, I need to delete 1 item when I upload success a file call deleteCallWhenUploadSuccess.
In this class, I using fileName to determined item need to delete.
But it doesn't delete item in ListView from Layout activity_call_history.xml
In DAO class I delete with code:
public void deleteCallWhenUploadSuccess(String fileNameWhis)
{
db = callDatabaseHelper.getWritableDatabase();
String where = CallDatabaseHelper.FILE_NAME + "='" + fileNameWhis + "'";
db.delete(CallDatabaseHelper.TABLE_NAME, where, null);
}
And in other class I using it:
DAO.deleteCallWhenUploadSuccess(filename);
I write code to remove an item on ListView in event onActionItemClicked.
On the DAO class I start to delete an item with rowId:
public void deleteCallV2(int rowId) {
db = callDatabaseHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from "+CallDatabaseHelper.TABLE_NAME+" where rowId = "+String.valueOf(rowId),null);
while(cursor.moveToNext()){
this.rowId = CallDatabaseHelper.ROW_ID +"="+cursor.getString(cursor.getColumnIndex(CallDatabaseHelper.ROW_ID));
}
db.delete(CallDatabaseHelper.TABLE_NAME, this.rowId, null);
}
Code I used to delete item in ListView of Layout historyAdapter write code in HistoryFragment.java, I was comment in code to easy read:
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.deleteAction:
// Calls getSelectedIds method from ListViewAdapter Class
selected = historyAdapter
.getSelectedIds();
// Captures all selected ids with a loop
for (int i = (selected.size() - 1); i >= 0; i--) {
if (selected.valueAt(i)) {
Call selecteditem = historyAdapter
.getItem(selected.keyAt(i));
// Remove selected items following the ids
historyAdapter.remove(selecteditem);
}
}
getFragmentManager().beginTransaction().replace(R.id.container,new HistoryFragment()).commit();
// Close CAB
mode.finish();
return true;
case R.id.allAction:
historyAdapter.toggleAll(listView);
Toast.makeText(getActivity(), "Đã chọn tất cả", Toast.LENGTH_SHORT).show();
return true;
default:
return false;
}
}
But it only work when I select item with long press and select item or select all to delete. It using ID to delete item.
Since I didn't got much but according to the heading of the question which says to remove an item in list I can suggest you to use notifyDataSetChanged() whenever you are removing a view so that your listview could update itself whenever its changed
A little code which demonstrate this task is
adapter = new MyListAdapter(this);
lv = (ListView) findViewById(android.R.id.list);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
AlertDialog.Builder adb=new AlertDialog.Builder(MyActivity.this);
adb.setTitle("Delete?");
adb.setMessage("Are you sure you want to delete " + position);
final int positionToRemove = position;
adb.setNegativeButton("Cancel", null);
adb.setPositiveButton("Ok", new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
MyDataObject.remove(positionToRemove);
adapter.notifyDataSetChanged();
}});
adb.show();
}
});
I need to delete an item permanently from ListView and then from database. I have a DatabaseHandler.java class, which has the delete function as:
// Deleting single contact, in DatabaseHandler.java class
public void deleteContact(Contact contact) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_CONTACTS, KEY_ID + " = ?",
new String[] { String.valueOf(contact.getID()) });
db.close();
}
Then I have a FriendList.java class, when the user's friends are displayed as an item in ListView. When I long press on an item, then I get the option of "Delete" and "Cancel" in Dialog Box. Now, when I click on delete, the item is deleted from the ListView, but, not from the database. How can I delete it from database as well?
The code for getting the option of "Delete" and "Cancel"
listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int position, long id) {
// TODO Auto-generated method stub
Intent i = new Intent(FriendList.this, Delete_Confirm.class).addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
//I am sending position of listitem in putExtra below//
i.putExtra("position", position);
startActivityForResult(i,CONFIRM);
item2 = (String) arg0.getItemAtPosition(position);
//Toast.makeText(FriendList.this, "Clicked"+item2, Toast.LENGTH_SHORT).show();
int l = item2.length();
c=0;
for(int j=0; j<=l; j++){
if(item2.charAt(j) != '9' || item2.charAt(j+1) != '1'){
c++;
}
else {
//Do nothing
break;
}
num = item2.substring(c, l);
}
Toast.makeText(FriendList.this, "Clicked: "+num, Toast.LENGTH_SHORT).show();
return true;
}
});
The corresponding code for onActivityResult is as follows:
#Override
public void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
switch (reqCode) {
case (CONFIRM) :
if(resultCode==RESULT_OK){
int posi = data.getIntExtra("position",0);
Log.d("msg","position is " + posi);
Log.d("msg","Do we reach here?");
final StableArrayAdapter adapter = new StableArrayAdapter(this,
android.R.layout.simple_list_item_1, list);
//db.deleteContact(posi);
list.remove(posi);
listview.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
break;
}}
Please suggest how can I delete it from database as well. Any help would be highly appreciated.
EDIT:
On uncommenting db.deleteContact(posi), I get the following error:
The method deleteContact(Contact) in the type DatabaseHandler is not applicable for the arguments (int)
Note that the function deleteContact has contact variable of the type Contact.
Its a compilation error.
You need to pass a Contact object to the method, not an integer.
When you delete.... Try Deleting first from database then from ListView..
example:
db.deleteContact(list.get(posi)); // this will get string
list.remove(posi);
DatabaseHandler class.......
public void deleteContact(String name){
Log.d("Name:",""+ name);
db.delete(TABLE_CONTACTS, KEY_NAME + " = ?", new String[] { name });
}
You can delete from database first then from ListView. And suggest Iterator to remove list element.
I think the problem is that position is not the corresponding id in the database. A possible solution would be to add a tag with the database id of the contact you have in this listitem. And when you remove it you get the tag with the id and delete the item from the database.
Edit (added clarification):
I would add in your listviewadapter something like:
yourcontactview.setTag(contact.getId());
in which you add to your view a tag with the database id of the corresponding contact.
Then where you delete the contact I would get the contact you want to delete with something like this:
Contact deletecontact = db.getContact(view.getTag());
db.deleteContact(deletecontact);
Of course you could change your
deleteContact(Contact contact) to a method in which you give the id instead of the contactobject.
This should hopefully work.
I am new to Android and started to work on databases and listview. I want to use subjectID as an ID of the listview I created. So that I can use that ID to query records from database when an item is tapped. I know this is asked many times but nothing seems to work on me. Thanks in advance for the help.
Part of the SQL: subjectID as _id
I have this piece of codes:
String[] dataColumns = { "subjectName","shortemail", "lastScanDate", "lastScore" } ;
int[] viewIDs = { R.id.list_column1 , R.id.list_column2, R.id.list_column3, R.id.list_column4 };
SimpleCursorAdapter adapter
= new SimpleCursorAdapter(
this, // The Context for the ListView
R.layout.subject_line, // Points to the XML for a list item
cursor, // The cursor to get items from
dataColumns,
viewIDs
);
setListAdapter(adapter);
db.close();
public void onListItemClick(ListView l, View v, int position, long id) {
//codes here
}
If you read the sourcecode of CursorAdapter, you can find:
public long getItemId(int position) {
if (mDataValid && mCursor != null) {
if (mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIDColumn);
} else {
return 0;
}
} else {
return 0;
}
}
And mRowIDColumn is assigned:
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
These mean, if you use "_id" as your database id, getItemId() will be already the database's Id.
Just fetch the row_ids and store in an ArrayList<Integer> arraylist = new ArrayList<Integer>;
Then in your onItemClick() insert this code:
row_id = arraylist.get(position);
startActivity(new Intent(PresentActivity.this, NextActivity.class).putExtra("row_id", row_id));
Hope this will help you.
If you need further help just let me know.
This is not the only way, we can achieve this. It is the simplest way.
OK, I've read around and see that Java only passes by value, not by reference so I don't know how to accomplish this.
I've 6 Spinners in an Android Activity that are populated with different SQLite queries.
The code to populate each Spinner and set the OnItemSelectedListener is very similiar so I was hoping to refactor to one method and call it 6 times with each Spinner ID and Sqlite query.
How do I get the Spinner onItemSelectedListener to change the right instance member on each different Spinner?
public void fillSpinner(String spinner_name, final String field_name) {
// This finds the Spinner ID passed into the method with spinner_name
// from the Resources file. e.g. spinner1
int resID = getResources().getIdentifier(spinner_name, "id",
getPackageName());
Spinner s = (Spinner) findViewById(resID);
final Cursor cMonth;
// This gets the data to populate the spinner, e.g. if field_name was
// strength = SELECT _id, strength FROM cigars GROUP BY strength
cMonth = dbHelper.fetchSpinnerFilters(field_name);
startManagingCursor(cMonth);
String[] from = new String[] { field_name };
int[] to = new int[] { android.R.id.text1 };
SimpleCursorAdapter months = new SimpleCursorAdapter(this,
android.R.layout.simple_spinner_item, cMonth, from, to);
months.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
s.setAdapter(months);
// This is setting the Spinner Item Selected Listener Callback, where
// all the problems happen
s.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
Cursor theCursor = (Cursor) parent.getSelectedItem();
// This is the problem area.
object_reference_to_clas_member_of_field_name = theCursor
.getString(theCursor.getColumnIndex(field_name));
}
public void onNothingSelected(AdapterView<?> parent) {
// showToast("Spinner1: unselected");
}
});
}
You call this method like this fillSpinner("spinner1","strength");.
It finds the spinner with id spinner1 and queries the database for the strength field. field_name, which is strength in this example had to be declared a final variable to be used in the onItemSelectedListener or I'd get the error Cannot refer to a non-final variable field_name inside an inner class defined in a different method.
But how do I get the onItemSelectedListener to change the value of a different instance member when each different Spinner is used? This is the all important line of code:
object_reference_to_clas_member_of_field_name = theCursor .getString(theCursor.getColumnIndex(field_name));
I can't use a final String as the variable will obviously change when the user selects a different value. I've read around a good bit and am stumped to a solution. I can just copy and paste this code 6 times and forget about refactoring but I'd really like to know the elegant solution. Post a comment if you don't understand my question, I'm not sure if I explaned myself well.
You can do it, by passing additional class as parameter of fillSpinner method:
A. Create interface
public interface OnSpinnerValueSelected {
void onValueSelected(String selectedValue);
}
B. Change your method a bit:
public void fillSpinner(String spinner_name, final String field_name,
final OnSpinnerValueSelected valueChangeListener) {
// Prepare spinner
s.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
Cursor theCursor = (Cursor) parent.getSelectedItem();
valueChangeListener.onValueSelected(theCursor
.getString(theCursor.getColumnIndex(field_name)));
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
C. provide listener:
fillSpinner("spinner1","strength", new OnSpinnerValueSelected() {
public void onValueSelected(String selectedValue) {
yourObject.setField(selectedValue);
}
});
Refactor your listener to a new "class". Initialize with the right arguments/instances as required so that the repeated "code" is reusuable.
Right, this is how I managed it but I'm still open to new suggestions for an accepted answer and I also created a bounty.
I didn't create a new class like panzerschreck suggested so I'm posting this as a new answer to my own question. Bit of a hack but I just created an if..then..else statement in the listener to check what spinner was selected and then set a different instance member.
s.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
Cursor theCursor = (Cursor) parent.getSelectedItem();
if (field_name.equalsIgnoreCase("strength")) {
strength=theCursor.getString(theCursor.getColumnIndex(field_name));
} else if (field_name.equalsIgnoreCase("ring")) {
ring_gauge=theCursor.getString(theCursor.getColumnIndex(field_name));
} else if (field_name.equalsIgnoreCase("country")) {
country=theCursor.getString(theCursor.getColumnIndex(field_name));
} else if (field_name.equalsIgnoreCase("wrapper")) {
wrapper=theCursor.getString(theCursor.getColumnIndex(field_name));
} else if (field_name.equalsIgnoreCase("length")) {
length=theCursor.getString(theCursor.getColumnIndex(field_name));
} else if (field_name.equalsIgnoreCase("price")) {
price=theCursor.getString(theCursor.getColumnIndex(field_name));
}
// showToast(category);
}
public void onNothingSelected(AdapterView<?> parent) {
// showToast("Spinner2: unselected");
}
});
Here are the class members
private String strength,ring_gauge,country,wrapper,length,price;
Bit of hack but without Java allowing objects to be really passed by reference, it's all I could do.