Android RecyclerView重复添加2条条目 新增时覆盖旧数据问题
Android待办应用RecyclerView重复条目、旧数据覆盖问题修复
问题现象
- 点击添加待办按钮后,RecyclerView每次生成2条完全相同的条目
- 添加新条目时,之前已存在的旧条目会被全部覆盖
根因定位
所有核心问题都出在MainActivity的添加按钮点击逻辑中,共4处错误:
- Adapter重复初始化:每次点击添加按钮都会执行
todoAdapter = TodoAdapter(mutableListOf(todo))创建全新的适配器实例,新适配器初始化时只传入当前新条目,之前保存在旧适配器里的所有历史数据全部丢失,直接导致旧数据被覆盖。 - 同一条目重复添加:新建适配器时已经把当前todo对象放进了适配器的初始数据列表,后续又调用
todoAdapter.addTodo(todo)把同一个todo对象再添加一次,单条数据被插入两次,导致重复显示。 - 代码括号错位:删除按钮的
setOnClickListener被错误写在添加按钮的点击回调内部,每次点击添加都会重复给删除按钮绑定一次监听,后续触发删除时会重复执行删除逻辑。 - 数据库插入逻辑失效:点击添加时先执行了
etTodoTitle.text.clear()清空输入框,再调用insertDataToDataBase,该方法内部重新读取输入框内容做非空校验时,输入框内容已经为空,校验逻辑完全无效。
另外Adapter里还有2处隐藏隐患:
- CheckBox设置勾选监听时没有先清空旧监听,列表滑动复用时会触发错乱的回调
- 勾选状态赋值逻辑错误,回调里直接对原有状态取反,会导致实际存储的勾选状态和CheckBox显示状态相反
修复代码
1. 修正MainActivity逻辑
class MainActivity : AppCompatActivity() { private lateinit var todoAdapter: TodoAdapter private lateinit var todoViewModel: TodoViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) rvTodoItems.layoutManager = LinearLayoutManager(this) // 适配器仅初始化一次,绑定到RecyclerView,初始数据为空 todoAdapter = TodoAdapter(mutableListOf()) rvTodoItems.adapter = todoAdapter btnAddTodo.setOnClickListener { val todoTitle = etTodoTitle.text.toString().trim() if (todoTitle.isNotEmpty()) { val todo = Todo(0, todoTitle, false) // 仅调用一次添加方法,不重复插入 todoAdapter.addTodo(todo) // 先执行数据库插入,再清空输入框 insertDataToDataBase(todo) etTodoTitle.text.clear() } } // 删除按钮监听移到添加按钮回调外部,避免重复绑定 btnDeleteTodo.setOnClickListener { todoAdapter.deleteDoneTodos() } } private fun insertDataToDataBase(todo: Todo) { // 直接使用传入的todo对象做校验,不重复读取输入框 if (todo.title.isNotEmpty()) { todoViewModel.addTodoToDataBase(todo) Toast.makeText(this, "Successfully Added", Toast.LENGTH_LONG).show() } else { Toast.makeText(this, "Please fill out all fields", Toast.LENGTH_LONG).show() } } private fun inputCheck(todoTitle:String):Boolean { return todoTitle.isNotEmpty() } }
2. 修正Adapter的onBindViewHolder逻辑
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) { val curTodo = todos[position] holder.itemView.apply { // 先清空旧的勾选监听,避免复用错乱 cbDone.setOnCheckedChangeListener(null) tvTodoTitle.text = curTodo.title cbDone.isChecked = curTodo.isChecked toggleStrikeThrough(tvTodoTitle, curTodo.isChecked) cbDone.setOnCheckedChangeListener { _, isChecked -> toggleStrikeThrough(tvTodoTitle, isChecked) // 直接用回调返回的最新状态赋值,不要取反 curTodo.isChecked = isChecked } } }
内容的提问来源于stack exchange,提问作者Clivityy
相关产品推荐
相关产品推荐

