Android Studio中RecyclerView用ListAdapter遇IndexOutOfBoundsException崩溃问题
核心矛盾:自定义数据状态与ListAdapter内部状态不一致
你的Adapter存在关键问题:手动维护size变量控制条目数量和视图类型,却忽略了ListAdapter本身依赖currentList管理数据源的核心机制,导致数据状态和视图状态出现冲突。
具体问题拆解
getItemCount的错误重写
当size=0时,你让getItemCount返回1(用于显示空布局),但此时ListAdapter内部的currentList是空的。submitList方法会基于新旧currentList执行DiffUtil差异计算,这就出现了「Adapter告诉RecyclerView有1个条目,但实际数据源无数据」的矛盾。
调用无回调的submitList(new ArrayList(myNewList))时,DiffUtil会按内部空列表计算更新操作,RecyclerView却按照getItemCount的结果去绑定视图,最终触发位置越界——因为实际数据源没有对应位置的数据。带
notifyDataSetChanged的回调能避免崩溃的原因
在submitList的回调里调用adapter.notifyDataSetChanged()时,这个操作会强制RecyclerView完全刷新所有视图,跳过DiffUtil的局部更新逻辑。相当于让RecyclerView直接重新读取getItemCount和getItemViewType的结果,暂时掩盖了「数据源和条目数量不一致」的问题,但这只是治标不治本的临时方案。
错误日志对应解释
日志中的IndexOutOfBoundsException,是因为RecyclerView根据getItemCount认为存在1个条目,但ListAdapter内部的currentList是空的,导致视图持有者的位置计算混乱,超出了实际数据源的范围。
正确修复方案
- 移除手动维护的
size变量,直接用ListAdapter提供的getCurrentList()判断数据状态:// 删除setSize方法 @Override public int getItemCount() { // 列表为空时返回1(空布局),否则返回实际条目数 return getCurrentList().isEmpty() ? 1 : getCurrentList().size(); } @Override public int getItemViewType(int position) { // 空布局场景下position固定为0 if (getCurrentList().isEmpty()) { return TYPE_EMPTY; } else { return TYPE_ONE; } } - ViewModel观察代码简化:无需手动设置size,直接提交列表即可:
customerRelationObserver = list -> { adapter.submitList(list); }; - 更优雅的空布局实现(可选):推荐使用外部空视图机制——在布局中添加空视图(如TextView),列表为空时显示空视图、隐藏RecyclerView;列表有数据时隐藏空视图、显示RecyclerView。这种方式不需要在Adapter中特殊处理条目数,逻辑更清晰。
内容的提问来源于stack exchange,提问作者Jeff Lin

