尝试从表/列表中删除项目时,无法将SQLite Row ID与ListView位置匹配

时间:2017-09-03 19:30:34

标签: android listview android-sqlite

之前已经提出过这个问题,但迄今为止没有一个实施对我有所帮助。

我正在构建一个应用程序,我在listview中显示我的项目,使用SQLite进行持久化。我能够动态地将项目添加到我的列表视图并成功将它们存储在我的数据库中,但是我无法从屏幕或表格中删除它们。我知道原因。我的SQLite Row ID与我的ListView不匹配。但另一个问题是我仍然能够从我的屏幕和我的表中删除与SQLite Row ID匹配的位置的项目(例如,我在列表中的第3个待办事项),但我无法删除任何内容

这是我应该从数据库中删除项目的方法:

public boolean itemDeleteFromDatabase(long id) {
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        boolean databaseDelete = database.delete(TABLE_NAME, TO_DO + "=?" + id, null) > 0;
        listItems.setAdapter(adapter);
        return databaseDelete;
    }

我从OnItemLongClick方法调用此方法,将ListView位置作为参数传递:

listItems.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> adapter, View item, int position, long id) {
                toDoItems.remove(position);
                itemDeleteFromDatabase(id);
                MainActivity.this.adapter.notifyDataSetChanged();
                return true;
            }
        });

这是堆栈跟踪。这个问题是它只解决了代码中的1个问题:

FATAL EXCEPTION: main
                                                                       Process: ca.ozbek.preworktodoapp, PID: 2105
                                                                       android.database.sqlite.SQLiteException: variable number must be between ?1 and ?999 (code 1): , while compiling: DELETE FROM student WHERE todo=?0

根据请求添加源代码

MainActivity.java

public class MainActivity extends AppCompatActivity {
    DatabaseHelper databaseHelper;
    private final int REQUEST_CODE = 10;
    ArrayList <String> toDoItems = new ArrayList<>();
    ArrayAdapter<String> adapter;
    ListView listItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listItems = (ListView) findViewById(R.id.listViewItems);
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, toDoItems);
        listItems.setAdapter(adapter);
        databaseHelper = new DatabaseHelper(this);
        getToDos();

        listItems.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> adapter, View item, int position, long id) {
                toDoItems.remove(position);
                itemDeleteFromDatabase(id + 1);
                MainActivity.this.adapter.notifyDataSetChanged();
                return true;
            }
        });

        listItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapter, View item, int pos, long id) {
                Intent intent = new Intent(MainActivity.this, EditItemActivity.class);
                intent.putExtra("item", toDoItems.get(pos));
                intent.putExtra("itemPos", String.valueOf(pos));
                startActivityForResult(intent, REQUEST_CODE);
            }
        });
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
            String item = data.getStringExtra("item");
            int itemPosition = Integer.parseInt(data.getStringExtra("itemPos"));
            toDoItems.add(itemPosition, item);
            toDoItems.remove(itemPosition + 1);
            adapter.notifyDataSetChanged();
        }
    }

    public void addItem(View v) {
        EditText newItem = (EditText) findViewById(R.id.itemInputEditText);

        if (newItem.getText().length() == 0) {
            Toast.makeText(this, "You need to enter a to do.", Toast.LENGTH_SHORT).show();
        } else {
            String item = newItem.getText().toString();
            databaseHelper.insertData(item);
            adapter.add(item);
            newItem.setText("");
        }
    }

    public  void getToDos(){
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        Cursor cursor = database.rawQuery("select * from student",null);
        if (cursor.moveToFirst()) {
            while (!cursor.isAfterLast()) {
                String name = cursor.getString(cursor.getColumnIndex("todo"));
                adapter.add(name);
                adapter.notifyDataSetChanged();
                cursor.moveToNext();
            }
        }
    }

    public boolean itemDeleteFromDatabase(Long id) {
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        boolean databaseDelete = database.delete(TABLE_NAME, TO_DO + "=?", new String[]{Long.toString(id)}) > 0;
        listItems.setAdapter(adapter);
        return databaseDelete;
    }
}

DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {

    public static final String DATABASE_NAME = "todo.db";
    public static final String TABLE_NAME = "student";
    public static final String ID = "id";
    public static final String TO_DO = "todo";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        String CREATE_TO_DO_TABLE = "CREATE TABLE "
                + TABLE_NAME
                + "("
                + ID
                + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                + TO_DO
                + " TEXT"
                + ")";
        sqLiteDatabase.execSQL(CREATE_TO_DO_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(sqLiteDatabase);
    }

    public boolean insertData(String todo) {
        SQLiteDatabase sqLiteDatabase = this.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(TO_DO, todo);
        long result = sqLiteDatabase.insert(TABLE_NAME, null, contentValues);

        if (result == -1) {
            return false;
        } else {
            return true;
        }
    }

    public Cursor getListContents() {
        SQLiteDatabase sqLiteDatabase = this.getWritableDatabase();
        Cursor data = sqLiteDatabase.rawQuery("SELECT * FROM " + TABLE_NAME, null);
        return data;
    }
}

1 个答案:

答案 0 :(得分:1)

SQL基本上是说您没有提供与 ? 匹配的参数。即

boolean databaseDelete = database.delete(TABLE_NAME, TO_DO + "=?" + id, null) > 0;

有效地说DELETE FROM table WHERE TO_DO =unobtainablevale 10

10是演示的虚构ID

您可以将其更改为

boolean databaseDelete = database.delete(TABLE_NAME, TO_DO + "=" + id, null) > 0; 

boolean databaseDelete = database.delete(TABLE_NAME, TO_DO + "=?", new String[]{Long.toString(id)}) > 0;

后者可能被认为是更好的。

P.S。未经测试,可能存在奇怪的拼写错误。

解决方案1使用SimpleCursorAdapter而不是ArrayAdpater

1)在DatabaseHelper中将public static final String ID = "id";更改为public static final String ID = "_id";(即添加下划线,建议不管使用哪种方法但是需要CursorAdapter)

注意!这将需要删除现有数据库。使用设置/应用程序,选择应用程序,然后清除数据或卸载应用程序。

2)添加用&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;到MainActivity(准备使用Cursor Adapter,ps会留下ArrayAdapter的东西,通常是asis但是必须删除一些)

ArrayList<String> toDoItems = new ArrayList<>();
ArrayAdapter<String> adapter;
SimpleCursorAdapter altadapter;     //<<<<<<<<<
Cursor itemlistcursor;              //<<<<<<<<<
ListView listItems;

3)为onDestroy方法添加覆盖(不需要但是清理光标): -

@Override
public void onDestroy() {
    super.onDestroy();
    itemlistcursor.close();
}

4)添加替代方法,例如getItemListAsCursor获取数据: -

public void getItemListAsCursor() {
    SQLiteDatabase database = databaseHelper.getWritableDatabase();
    itemlistcursor = database.query(TABLE_NAME,null,null,null,null,null,null);
}

注意!使用query方法代替rawQuery但等同于SELECT * FROM student;

5)将itemDeleteFromDatabase更改为使用ID列而不是TODO列(之前没有发现过),并根据以下代码注释掉行: -

public boolean itemDeleteFromDatabase(Long id) {
    SQLiteDatabase database = databaseHelper.getWritableDatabase();
    boolean databaseDelete = database.delete(TABLE_NAME, ID + "=?", new String[]{Long.toString(id)}) > 0;
    //listItems.setAdapter(adapter);
    return databaseDelete;
}

6)注释掉下面的行(摆脱使用ArrayAdapater): -

    listItems = (ListView) findViewById(R.id.listViewItems);
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, toDoItems);
    //listItems.setAdapter(adapter);
    databaseHelper = new DatabaseHelper(this);
    //getToDos();

7)更改onItemLongClickListener,如下所示

    listItems.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<?> adapter, View item, int position, long id) {
            //toDoItems.remove(position);
            itemDeleteFromDatabase(id);         //<<<<<<
            getItemListAsCursor();              //<<<<<<
            //MainActivity.this.adapter.notifyDataSetChanged();
            altadapter.swapCursor(itemlistcursor);  //<<<<<<
            return true;
        }
    });

请注意!可以保留notifyDatasetChanged(我只是更喜欢swapCursor);

8)最后在注释掉//getToDos行之后添加以下内容: -

    getItemListAsCursor();
    altadapter = new SimpleCursorAdapter(this,
            android.R.layout.simple_list_item_1,
            itemlistcursor,
            new String[]{ TO_DO},
            new int[]{android.R.id.text1},
            0);
    listItems.setAdapter(altadapter);

使用ArrayAdpater的解决方案2

1)为ID添加免费数组(根据//&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;行): -

    ArrayList <String> toDoItems = new ArrayList<>();
    ArrayList<Long> toDoItemsID = new ArrayList<>(); //<<<<<<
    ArrayAdapter<String> adapter;
    ListView listItems;

2)更改DatabaseHelper中的insertData方法,通过将方法替换为: -

来返回id
public long insertData(String todo) {
    SQLiteDatabase sqLiteDatabase = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put(TO_DO, todo);
    return sqLiteDatabase.insert(TABLE_NAME, null, contentValues);
}

3)更改getToDos方法以将id存储到complimentary数组中(//&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;

public  void getToDos(){
    SQLiteDatabase database = databaseHelper.getWritableDatabase();
    Cursor cursor = database.rawQuery("select * from student",null);
    if (cursor.moveToFirst()) {
        while (!cursor.isAfterLast()) {
            String name = cursor.getString(cursor.getColumnIndex(TO_DO));
            adapter.add(name);
            toDoItemsID.add(cursor.getLong(cursor.getColumnIndex(ID))); //<<<<<<
            adapter.notifyDataSetChanged();
            cursor.moveToNext();
        }
    }
}

请注意!我还将"todo"替换为TO_DO

4)更改addItem方法以存储ID

public void addItem(View v) {
    EditText newItem = (EditText) findViewById(R.id.itemInputEditText);

    if (newItem.getText().length() == 0) {
        Toast.makeText(this, "You need to enter a to do.", Toast.LENGTH_SHORT).show();
    } else {
        String item = newItem.getText().toString();
        //databaseHelper.insertData(item);                  //OLD
        toDoItemsID.add(databaseHelper.insertData(item));   //<<<<<<<
        adapter.add(item);
        newItem.setText("");
    }
}

请注意!我完全不喜欢这个,我可以设想保持toDoItemsID同步的问题,此外,目前还可以满足未插入的要求(从插入数据的返回应该很容易做&gt; 0)。

5)最后onItemLongClickListener发生了变化: -

    listItems.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<?> adapter, View item, int position, long id) {
            itemDeleteFromDatabase(toDoItemsID.get(position)); //<<<<<<
            toDoItems.remove(position);
            toDoItemsID.remove(position);  //<<<<<<
            //itemDeleteFromDatabase(id + 1); // REMOVE
            MainActivity.this.adapter.notifyDataSetChanged();
            return true;
        }
    });

我已经对上述内容进行了测试,但在复制时可能会无意中遗漏错误。