Android中ListView长按弹窗删除SQLite列表项问题求助
解决ListView长按删除对应SQLite数据的问题
兄弟,我仔细看了你的代码,核心问题确实是没有正确绑定ListView项和数据库里的主键ID,加上删除方法的SQL逻辑完全错了,导致删不对数据甚至乱改数据。下面一步步给你修正:
先说说现有代码的几个致命问题
theList只存了文本(COL2的值),没存对应的主键intWhereToGoListOID,长按的时候根本拿不到要删除的那条数据的唯一标识ReadID方法靠游标移动计数来拿ID,逻辑混乱,一旦数据库有数据删除,ListView的position和数据库的行顺序就不匹配了deleteEntry方法的SQL完全错了:你用COL2 = position来删数据,还把COL2的值减1,这相当于把文本改成了数字,完全偏离了删除主键对应数据的逻辑
正确的实现步骤
1. 创建数据模型类,存储ID和显示文本
首先,我们需要把每条数据的ID和文本绑定在一起,而不是只存字符串:
public class ListItem { private int id; private String content; public ListItem(int id, String content) { this.id = id; this.content = content; } public int getId() { return id; } public String getContent() { return content; } // 重写toString,让ArrayAdapter能直接显示文本 @Override public String toString() { return content; } }
2. 修改数据源加载逻辑,同时获取ID和文本
把原来的ArrayList<String>改成ArrayList<ListItem>,查询数据库时同时把主键ID和COL2的值存进去:
// 替换原来的ArrayList<String> final ArrayList<ListItem> theList = new ArrayList<>(); final Cursor data = myDB.getListContents(); if (data.getCount() == 0) { Toast.makeText(ViewListContents.this, "Your list is empty :(", Toast.LENGTH_LONG).show(); } else { while (data.moveToNext()) { // 获取主键ID(intWhereToGoListOID对应的列) int id = data.getInt(data.getColumnIndex(DatabaseHelper.intWhereToGoListOID)); // 获取显示文本(COL2) String content = data.getString(data.getColumnIndex(DatabaseHelper.COL2)); theList.add(new ListItem(id, content)); } // 这里只需要设置一次适配器 ListAdapter listAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, theList); listView.setAdapter(listAdapter); }
3. 修复DatabaseHelper里的deleteEntry方法
删除数据必须用主键intWhereToGoListOID来定位,绝对不能碰COL2的值:
// 替换你原来的deleteEntry方法 public void deleteEntry(int itemId) { SQLiteDatabase sqLiteDatabase = getWritableDatabase(); // 用主键作为条件删除,参数化查询防止SQL注入 sqLiteDatabase.delete(TABLE_TWHERETOGOLIST, intWhereToGoListOID + " = ?", new String[]{String.valueOf(itemId)}); sqLiteDatabase.close(); }
4. 修改ListView长按事件,获取正确ID并执行删除
现在长按的时候可以直接拿到对应ListItem的ID,然后调用正确的删除方法,再更新ListView:
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View v, final int position, final long id) { PopupMenu p = new PopupMenu(ViewListContents.this, v); MenuInflater inflater = p.getMenuInflater(); inflater.inflate(R.menu.popup_layout, p.getMenu()); p.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.delete_item: // 获取选中的ListItem对象 ListItem selectedItem = theList.get(position); int itemId = selectedItem.getId(); // 1. 删除数据库里的数据 myDB.deleteEntry(itemId); // 2. 删除ListView数据源里的项 theList.remove(position); // 3. 通知适配器更新UI ((ArrayAdapter) listView.getAdapter()).notifyDataSetChanged(); Toast.makeText(getApplicationContext(), "Item deleted", Toast.LENGTH_SHORT).show(); return true; case R.id.edit_item: // 这里可以加编辑逻辑 return true; default: return false; } } }); p.show(); return true; } });
5. 清理无用代码
把原来的ReadID方法、错误的deleteEntry实现都删掉,还有代码里那些注释掉的混乱逻辑也可以清理了。
关键注意点
- 永远用数据库的主键来定位要删除/修改的数据,不要用position或者文本,因为position会随着ListView数据变化而改变,文本可能重复
- 数据源和数据库要保持同步:删数据库的同时也要删数据源里的项,然后调用
notifyDataSetChanged更新UI - 用参数化查询(
?占位符)来写SQL,避免SQL注入,同时防止语法错误
内容的提问来源于stack exchange,提问作者Noah
相关产品推荐
相关产品推荐

