Android ListView自定义适配器删除数据库项后如何自动刷新
问题根因
你现在遇到的列表不刷新、ImageView点击无响应的问题核心是3个:
- ImageView作为列表项内的可点击子控件,会默认消费点击事件,所以你写在
ListView.onItemClickListener里的逻辑根本不会在点击ImageView时触发 - 你在适配器里给ImageView写的点击逻辑,仅执行了数据库删除操作,没有更新适配器持有的数据源,也没有触发列表刷新通知,所以数据库数据变了界面不会同步
- 现有代码存在两个功能性bug:一是
deleteOne方法错误使用rawQuery执行DELETE语句,实际上不会生效删除操作;二是读取数据库数据时没有给DataModel设置主键ID,按title删除会出现重名误删问题,且适配器里new新的DataModel会丢失ID标识。
修复步骤
1. 修复数据库操作类的逻辑bug
首先修改DataBaseHelper,修正删除逻辑,查询数据时把主键ID赋值给数据模型,避免误删:
// 修正getAllAsList方法,读取ID并赋值给模型 public ArrayList<DataModel> getAllAsList() { ArrayList<DataModel> returnList = new ArrayList<>(); String queryString = "SELECT * FROM " + TODO_TABLE; SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor = db.rawQuery(queryString, null); if(cursor.moveToFirst()) { do { // 读取主键ID int todoId = cursor.getInt(0); String todoTitle = cursor.getString(1); DataModel newTodo = new DataModel(todoTitle); newTodo.setId(todoId); returnList.add(newTodo); } while(cursor.moveToNext()); } cursor.close(); db.close(); return returnList; } // 修正deleteOne方法,用ID做唯一条件执行删除 public boolean deleteOne(DataModel dModel) { SQLiteDatabase db = this.getWritableDatabase(); // 用SQLite自带delete方法,参数化查询避免SQL注入,按主键删除不会误删 int affectedRows = db.delete(TODO_TABLE, COLUMN_ID + " = ?", new String[]{String.valueOf(dModel.getId())}); db.close(); return affectedRows > 0; }
额外修复:你原来的空输入判断正则少了转义符,"s*"要改成"\\s*"才能正确匹配空字符串。
2. 修改适配器逻辑,删除后触发列表刷新
修改DataAdapter,复用列表项布局优化性能,删除数据库条目后同步更新数据源、通知列表刷新,不要在getView里重复创建丢失ID的DataModel实例:
publicpublic class DataAdapter extends ArrayAdapter<DataModel> { private Context mContext; private int mResource; private ArrayList<DataModel> mList; private DataBaseHelper mDbHelper; public DataAdapter(Context context, int resource, ArrayList<DataModel> list, DataBaseHelper dbHelper) { super(context, resource, list); mContext = context; mResource = resource; mList = list; mDbHelper = dbHelper; } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { // 复用convertView,避免每次滑动都inflate布局造成卡顿 if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(mContext); convertView = inflater.inflate(mResource, parent, false); } // 直接获取当前位置的真实数据,不要new新对象丢失ID DataModel currentItem = getItem(position); TextView tvTitle = convertView.findViewById(R.id.task); ImageView delView = convertView.findViewById(R.id.delView); tvTitle.setText(currentItem.getTitle()); delView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 第一步:删除数据库对应条目 mDbHelper.deleteOne(currentItem); // 第二步:移除适配器数据源里的对应条目 mList.remove(position); // 第三步:通知适配器数据变更,ListView自动刷新 notifyDataSetChanged(); } }); return convertView; } }
3. 优化MainActivity逻辑,避免重复创建适配器
不需要每次增删数据都重新new一个适配器,初始化一次即可,增删后直接刷新数据:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new DataBaseHelper(MainActivity.this); assignVariables(); registerClick(); // 只初始化一次适配器 todoAdapter = new DataAdapter(MainActivity.this, R.layout.list_item, dbHelper.getAllAsList(), dbHelper); lv.setAdapter(todoAdapter); } // 替换原来的showAllToDos为刷新方法 public void refreshTodoList() { todoAdapter.clear(); todoAdapter.addAll(dbHelper.getAllAsList()); todoAdapter.notifyDataSetChanged(); }
添加待办的逻辑里,数据库插入完成后直接调用refreshTodoList()即可,原来写的lv.setOnItemClickListener如果没有点击整个列表项的其他需求可以直接删除,删除逻辑已经在ImageView的点击监听里处理完成。
内容的提问来源于stack exchange,提问作者Nicole
相关产品推荐
相关产品推荐

