从Android数据库中删除项目 - 光标索引超出界限异常

时间:2014-08-24 11:31:19

标签: java android android-listview android-sqlite android-cursor

在我的Android应用程序中,我试图从数据库中删除一个项目,在列表视图中选择了相应的项目,并从长按下创建的上下文菜单中选择一个项目。 listview正在从arraylist中填充,该arraylist是从数据库中的项目创建的。

但是我在尝试删除列表视图中的第一项以外的任何内容时都会收到错误(这实际上是最近添加的项目,最后一项是在arraylist中,因为listview以相反的顺序显示列表中的项目)。如果我尝试删除最新项目以外的项目,则会收到错误,android.database.CursorIndexOutOfBoundsException:请求索引0,大小为0.

发生此错误后,如果我尝试打开包含列表视图的活动,我也会收到相同的错误,直到我删除应用的数据并再次打开它。

如果光标为空并且它正在移动到下一行但到目前为止没有运气,我已经尝试检入databasehandler deleteGoalFromDb方法

在上下文菜单中按下删除项目时调用的方法(它是菜单中唯一的项目):

@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;
    }

我的deleteGoal()方法:

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;

    }

使用(int,String,int,int,int,int,boolean,String)创建目标。

这是我的错误logcat:

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)

3 个答案:

答案 0 :(得分:1)

就像Frank N. Stein已经说过:你需要使用你想要删除的目标的数据库ID。所以在onContextItemSelected上写这样的东西:

int idToDelete = listView.getItem(position).getGoalId();
deleteGoal(idToDelete);

答案 1 :(得分:1)

readGoalFromDb(int id)

 Goal goal = null;
 if (cursor != null)
       if(cursor.moveToFirst()) // IMPORTANT!, check whether the returned cursor is empty
       {

           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));
            return goal;
        }
        else {
           //the returned cursor is empty
             Log.v("cursorempty","the cursor was returned empty");
             return null
        }

您检查了null但是您没有检查光标是否有项目。您只是尝试检索导致CursorIndexOutOfBoundsException的值。

如果返回的cursor为空

,请采取适当的措施

将您的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++) { 
            if(null != db.readGoalFromDb(i)) 
            goalsList.add(db.readGoalFromDb(i));
        }
    }

答案 2 :(得分:0)

对于其他遇到同样问题的人来说,我最终是如何解决它的。 在我的数据库处理程序类中,我创建了一个新方法,该方法将遍历目标表中的所有项目,如果现在找到该行(已被删除),则跳过它并递增计数器,如果找到该行,则将目标添加到一个arraylist。如果一行中空行的计数器达到25,则光标停止搜索,并且我删除了所有空白项目的arraylist。

通过添加验证来检查光标是否为空并且跳过该项目(如果是)则它会停止,因为尝试到达错误的项目或空指针异常时存在游标索引错误。通过在活动中使用deleteGoal中的idGoal值,意味着它不会引用空白行。

创建目标的arraylist的新方法:(在数据库处理程序类中)

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;
    }

我还改变了一些其他方法以适应这个并清理代码。

新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;

    }

新的deleteGoal方法:

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);
    }

数据库处理程序类中的deleteGoalFromDb方法保持不变。