In my android application I am attempting to delete an item from a database, having selected the corresponding item in a listview and selecting an item from the context menu created on long press. The listview is being populated from an arraylist, which is being created from the items in the database.
However I am getting an error when attempting to delete anything but the first item in the listview (this is actually the most recently added item and last in the arraylist as the listview is displaying the items in the list in reverse order). If I try to delete an item other than the latest item then I get an error, android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0.
After this error I also get the same error if I attempt to open the activity containing the listview, until I delete the app's' data and open it again.
I have tried checking in the databasehandler deleteGoalFromDb method if the cursor is empty and if it is moving to the next row but have had no luck so far
Method called when the delete item is pressed in the context menu (it is is the only item in the menu):
@Override
public boolean onContextItemSelected(MenuItem item) { //what to do when menu item clicked in context menu
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
//call delete goal and make it delete goal
//System.out.println(info.position+"****************");
int position_actual = goalsList.size() - info.position; //needed as list is displayed in reverse order
//System.out.println(position_actual+"----------------------");
deleteGoal(position_actual);
return true;
}
My deleteGoal() method:
public void deleteGoal(int id) {
db.deleteGoalFromDb(id); //deletes the goal from the database
makeGoalsFromDB(); //refreshes the goalsList
}
deleteGoalFromDb:
public void deleteGoalFromDb(int id){
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_GOALS, KEY_ID + " = ?", new String[] {id+""});
db.close();
}
makeGoalsFromDB:
public void makeGoalsFromDB() {
//looping through the table, get the goals and add them to the goals list
goalsList = new ArrayList<Goal>();
int goalsNo = db.getGoalsCount();
for (int i = 1; i <= goalsNo; i++) {
goalsList.add(db.readGoalFromDb(i));
}
}
readGoalFromDb:
public Goal readGoalFromDb(int id) { //method to get a goal from the database
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.query(TABLE_GOALS, new String[] { KEY_ID,
KEY_STROKE, KEY_TIME_HRS, KEY_TIME_MIN, KEY_TIME_SECS, KEY_DISTANCE, KEY_ACHIEVED, KEY_DATE_GOAL }, KEY_ID + "=?",
new String[] { String.valueOf(id) }, null, null, null, null);
if (cursor != null)
cursor.moveToFirst();
Goal goal = new Goal(cursor.getInt(0), cursor.getString(1), cursor.getInt(2),
cursor.getInt(3), cursor.getInt(4),
cursor.getInt(5), Boolean.parseBoolean(cursor.getString(6)), cursor.getString(7));
cursor.close();
db.close();
return goal;
}
A goal is created with (int, String, int, int, int , int, boolean, String).
Here is my logcat for the error:
08-24 12:29:37.850: E/AndroidRuntime(7897): FATAL EXCEPTION: main
08-24 12:29:37.850: E/AndroidRuntime(7897): Process: com.lyncht.swimtracker, PID: 7897
08-24 12:29:37.850: E/AndroidRuntime(7897): android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.database.AbstractCursor.checkPosition(AbstractCursor.java:426)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.database.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:68)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.lyncht.swimtracker.DatabaseHandler.readGoalFromDb(DatabaseHandler.java:128)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.lyncht.swimtracker.ViewGoals.makeGoalsFromDB(ViewGoals.java:76)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.lyncht.swimtracker.ViewGoals.deleteGoal(ViewGoals.java:68)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.lyncht.swimtracker.ViewGoals.onContextItemSelected(ViewGoals.java:61)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.app.Activity.onMenuItemSelected(Activity.java:2620)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback.onMenuItemSelected(PhoneWindow.java:3864)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:741)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:884)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.view.menu.MenuDialogHelper.onClick(MenuDialogHelper.java:167)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.app.AlertController$AlertParams$3.onItemClick(AlertController.java:941)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.widget.AdapterView.performItemClick(AdapterView.java:299)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.widget.AbsListView.performItemClick(AbsListView.java:1113)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.widget.AbsListView$PerformClick.run(AbsListView.java:2911)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.widget.AbsListView$3.run(AbsListView.java:3645)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.os.Handler.handleCallback(Handler.java:733)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.os.Handler.dispatchMessage(Handler.java:95)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.os.Looper.loop(Looper.java:136)
08-24 12:29:37.850: E/AndroidRuntime(7897): at android.app.ActivityThread.main(ActivityThread.java:5001)
08-24 12:29:37.850: E/AndroidRuntime(7897): at java.lang.reflect.Method.invokeNative(Native Method)
08-24 12:29:37.850: E/AndroidRuntime(7897): at java.lang.reflect.Method.invoke(Method.java:515)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
08-24 12:29:37.850: E/AndroidRuntime(7897): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
08-24 12:29:37.850: E/AndroidRuntime(7897): at dalvik.system.NativeStart.main(Native Method)
For anyone else having the same problem here is how I fixed it in the end. In my database handler class I created a new method that would go through all items in the goals table and if the row was now found (had been deleted) skipped past it and incremented a counter, and if the row was found add the goal to an arraylist. If the counter of empty rows in a row reached 25 the cursor stopped searching, and I stripped the arraylist of all blank items.
By adding validation to check if the cursor was empty and skipping past the item if it was then it stops there being a cursor index error from trying to reach the wrong item or a null pointer exception. By using the idGoal value in deleteGoal in the actiivty it means that it will not reference blank rows.
New method to create arraylist of goals: (inside the database handler class)
public ArrayList<Goal> readAllGoals(){
ArrayList<Goal> goalReturnList = new ArrayList<Goal>();
SQLiteDatabase db = this.getWritableDatabase();
String selectQuery = "SELECT * FROM " + TABLE_GOALS; // Query to select all from goals table
Cursor cursor = db.rawQuery(selectQuery, null);
int counter = 0;
int noFalse = 0;
boolean z = true;
while(z) { //the loop will check if there is a record or not and skip past blank records
if(cursor.moveToPosition(counter)&&noFalse<25){ //if it can get to the next position and there has not been 25 missing in a row
noFalse = 0; //Reset the counter for number of missing in a row
Goal goal = new Goal(cursor.getInt(0), cursor.getString(1), cursor.getInt(2),
cursor.getInt(3), cursor.getInt(4),
cursor.getInt(5), Boolean.parseBoolean(cursor.getString(7)), cursor.getString(6)); //Create the goal to add
goalReturnList.add(goal); //Add it to the list
}else if(noFalse>=25){
z = false;
}else{
noFalse++; //Add to the number of false in a row
}
counter++; //Increment the counter
}
cursor.close();
db.close();
for (Goal g: goalReturnList){
if (g.strokeGoal.equals("")) { //strip blank goals
goalReturnList.remove(g);
}
}
return goalReturnList;
}
I also changed some of the other methods to fit in with this and clean up the code.
New onItemContextMenuSelected:
public boolean onContextItemSelected(MenuItem item) { //what to do when menu item clicked in context menu
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
deleteGoal(info.position);
return true;
}
New deleteGoal method:
public void deleteGoal(int position) {
Goal g = goalsList.get(position); //go to goal in goallist
db.deleteGoalFromDb(g.idGoal); //deletes the goal from the database, using its id stored in the object
//Below code will refresh the screen
ListView lview = (ListView) findViewById(R.id.GoalsTable);
populateList();
ListViewAdapter adapter = new ListViewAdapter(this, list);
lview.setAdapter(adapter);
}
The deleteGoalFromDb method inside the database handler class stayed the same.