嵌套RecyclerView+DiffUtil刷新子项状态异常问题求助
这是嵌套RecyclerView结合DiffUtil刷新时非常常见的问题,核心原因和RecyclerView默认的ItemAnimator行为有关,我来给你详细拆解:
1. 默认ItemAnimator的动画逻辑干扰了视图状态更新
RecyclerView的默认DefaultItemAnimator会对视图的增删改移操作添加过渡动画,在动画执行过程中,它会暂时保留旧的ViewHolder实例或者视图状态,用来完成动画的过渡效果。
在你的嵌套场景里,当父列表和子列表同时通过DiffResult刷新时,父列表的ItemAnimator可能会对子RecyclerView的容器视图执行动画(比如父项的布局变化动画),这个过程会意外地重置子ViewHolder的状态——比如你设置的highlighted高亮状态。动画的延迟处理会打断子视图的绑定逻辑,导致第一个子项的高亮状态被旧的视图覆盖。
2. DiffResult与ItemAnimator的时序冲突
DiffUtil是基于数据模型计算差异后通知RecyclerView更新视图,但ItemAnimator会在视图更新流程中插入动画环节,这两个过程的时序并不完全同步。
当你从“仅第一个子项高亮”切换到“两个子项都高亮”时,子列表的DiffResult已经计算出需要更新两个子项,但父列表的ItemAnimator可能还在处理上一次的动画,它会把还在动画中的旧视图状态“恢复”出来,导致第一个子项的高亮被错误重置,只有第二个子项的新状态能正常生效。
3. 关闭ItemAnimator解决问题的本质
当你给父、子RecyclerView都设置setItemAnimator(null)后,RecyclerView会跳过所有过渡动画,直接执行DiffResult对应的视图绑定操作。这样数据模型的变化会立即反映到视图上,没有动画过程中的状态干扰,自然就不会出现高亮被重置的问题了。
额外建议(如果需要保留动画)
如果你不想完全去掉动画,可以试试这两种方案:
- 自定义ItemAnimator:重写动画逻辑,确保在父项动画执行时,不会干扰子RecyclerView的视图状态,比如在动画开始前等待子列表的DiffResult刷新完成。
- 临时禁用变更动画:在刷新子列表前后,临时关闭父RecyclerView的变更动画:
// 刷新前关闭变更动画 parentRecyclerView.getItemAnimator().setSupportsChangeAnimations(false); // 执行子列表的DiffResult刷新操作 childAdapter.submitList(newChildren); // 刷新完成后恢复动画(可选) parentRecyclerView.getItemAnimator().setSupportsChangeAnimations(true);
这个方案需要测试兼容性,避免影响其他场景的动画效果。
内容的提问来源于stack exchange,提问作者piotrpawlowski

