You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android RecyclerView重复添加2条条目 新增时覆盖旧数据问题

Android待办应用RecyclerView重复条目、旧数据覆盖问题修复

问题现象

  • 点击添加待办按钮后,RecyclerView每次生成2条完全相同的条目
  • 添加新条目时,之前已存在的旧条目会被全部覆盖

根因定位

所有核心问题都出在MainActivity的添加按钮点击逻辑中,共4处错误:

  1. Adapter重复初始化:每次点击添加按钮都会执行todoAdapter = TodoAdapter(mutableListOf(todo))创建全新的适配器实例,新适配器初始化时只传入当前新条目,之前保存在旧适配器里的所有历史数据全部丢失,直接导致旧数据被覆盖。
  2. 同一条目重复添加:新建适配器时已经把当前todo对象放进了适配器的初始数据列表,后续又调用todoAdapter.addTodo(todo)把同一个todo对象再添加一次,单条数据被插入两次,导致重复显示。
  3. 代码括号错位:删除按钮的setOnClickListener被错误写在添加按钮的点击回调内部,每次点击添加都会重复给删除按钮绑定一次监听,后续触发删除时会重复执行删除逻辑。
  4. 数据库插入逻辑失效:点击添加时先执行了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:54:18