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

删除RecyclerView最后一项时应用崩溃报IndexOutOfBoundsException的原因与解决

问题根因定位
  • 崩溃触发链路完全匹配堆栈日志:IndexOutOfBoundsException: Index: 0, Size: 0 直接来自MainActivity删除按钮点击时的错误调用逻辑
  • 核心错误:删除操作中临时创建了全新的LevelAdapter实例调用删除方法,而非使用当前绑定到RecyclerView上的真实Adapter
  • 越界逻辑拆解:
    1. 执行顺序先删除SQLite条目,再通过getItemList()重新读取数据库,此时返回的列表已不包含刚删除的条目
    2. 当删除最后一个条目时,数据库被清空,getItemList()返回size=0的空列表,传入临时创建的新Adapter
    3. 对空列表执行removeAt(0)操作,直接触发数组越界异常
  • 次要逻辑缺陷:Adapter的deleteItem方法存在冗余刷新逻辑,空列表处理位置错误
修复方案

1. 重构MainActivity的Adapter持有逻辑

将RecyclerView绑定的Adapter提升为类成员变量,避免临时创建实例造成的数据源错位:

// 类成员变量声明
private lateinit var levelAdapter: LevelAdapter

// 修正后的RecyclerView初始化方法
private fun recyclerViewSetUp() {
    // 固定设置LayoutManager,无需重复创建
    binding.rvLevel.layoutManager = GridLayoutManager(this, 2)
    // 初始化Adapter时一次性绑定全量数据
    levelAdapter = LevelAdapter(getItemList(), this)
    binding.rvLevel.adapter = levelAdapter
}

2. 修正删除流程的调用逻辑

替换原有的错误删除代码,避免删除后重复读库、重复初始化列表:

tvDelete.setOnClickListener {
    // 从当前Adapter持有的数据源中获取对应位置的条目ID,无需重复查库
    val targetId = levelAdapter.todoList[position].id
    // 执行数据库删除
    MyDBHelper(this).deleteLabel(LevelModelClass(targetId, ""))
    // 操作当前展示的Adapter实例执行列表删除
    levelAdapter.deleteItem(position)
    customDialog.dismiss()
}

3. 修正Adapter的deleteItem方法

去掉冗余逻辑,增加边界判断,简化刷新流程:

fun deleteItem(position: Int) {
    // 增加越界保护
    if (position < 0 || position >= todoList.size) return
    todoList.removeAt(position)
    // 仅做局部刷新即可,无需调用全量notifyDataSetChanged
    notifyItemRemoved(position)
}
优化建议
  • 不要为了简化逻辑反复替换RecyclerView的Adapter实例,这种写法会造成不必要的性能损耗,还容易引发数据源错位的崩溃
  • 局部更新操作搭配notifyItemRemoved时,不要重复调用notifyDataSetChanged(),否则会覆盖列表的删除动画、重置所有视图的绑定状态
  • 所有涉及列表位置的操作,必须先校验position的合法性,避免因为RecyclerView视图复用、数据异步更新导致的位置错位问题

内容的提问来源于stack exchange,提问作者Mark Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 14:15:08