RecyclerView调用notifyItemRemoved抛异常,求正确调用时机
我开发了一款壁纸APP,通过API获取壁纸分类。在「分类」标签页中,我希望隐藏部分分类板块。理想情况下应由后端处理,但当前后端开发者无法配合,因此我自行实现。
最初我在RecyclerView的onBindViewHolder方法中判断,若分类标题为“Hot”则调用list.removeAt(holder.layoutPosition),但界面未更新;添加notifyItemRemoved(holder.layoutPosition)和notifyItemRangeChanged(holder.layoutPosition, list.size)方法后,出现“Cannot call this method while RecyclerView is computing a layout or scrolling”异常。
请问应在何处调用这些方法以避免该错误?
更新:我已在onResponse方法中通过categories.categories!!.removeAt(5)和categories.categories!!.removeAt(1)移除元素,再初始化RecyclerViewAdapter...
嘿,这个问题我之前踩过坑,咱们好好捋捋:
为什么最初的做法会报错?
onBindViewHolder是RecyclerView在布局计算或滚动过程中触发的方法——它正忙着绑定视图、计算每个item的位置呢,这时候你直接修改数据源并调用notify系列方法,相当于硬生生打断它的工作流程,自然会抛出那个异常。而且在这个时机改数据源,还可能导致列表索引混乱,出现布局错位的问题。
最稳妥的解决方式(就是你现在用的这个!)
在拿到API数据之后、初始化RecyclerView Adapter之前完成数据过滤,这才是正确的打开方式:
- 就像你更新里说的,在
onResponse回调里直接对返回的分类列表做移除/过滤操作,比如删掉不需要的第1、第5个元素,确认列表是你想要的状态后,再把处理好的列表传给Adapter并初始化。 - 这种方式下,Adapter从一开始拿到的就是干净的、已经过滤掉冗余分类的数据源,根本不需要在RecyclerView的绑定阶段去修改数据,完全避开了布局过程中操作数据的风险,代码逻辑也更清晰。
如果后续需要动态移除分类(比如用户操作触发)
要是之后有场景需要在APP运行中动态隐藏分类,那你得确保修改数据源和调用通知方法的时机是在RecyclerView的布局周期之外,比如用recyclerView.post { ... }把操作包裹起来:
// 示例:动态移除目标位置的分类 recyclerView.post { yourCategoryList.removeAt(targetPosition) yourAdapter.notifyItemRemoved(targetPosition) }
post方法会把你的任务放到RecyclerView的消息队列里,等它完成当前的布局或滚动操作后再执行,就不会触发那个讨厌的异常了。
内容的提问来源于stack exchange,提问作者Armen Hovhannisian

