RecyclerView 刷新踩坑记

工作中现在都是使用 RecyclerView,RecyclerView 中,经常使用到的几个刷新函数如下:

第 1 组

  • notifyDataSetChanged():无参,用于通知 Adapter 数据源发生变化并刷新。更新方式是所有 item 整体刷新,是最重的刷新方式
  • notifyItemChanged(int position):单参,用于通知在数据源中,位置处于 position 的 item 更新。更新方式是单个 item 整体刷新
  • notifyItemInserted(int position):单参,用于通知在数据源中,位置处于 position 的 item 之前有 item 插入,插入之后,position 位置的 item 位置变成了 position + 1。不过应该注意的是
    • 该方法使用时,应该首先变化数据源,再调用该方法
    • 该方法使用后,并不能真正插入一个 item,还应结合 notifyItemChanged 使用,才能真正插入一条数据。这个方法只是决定了像动画这些配置。
  • notifyItemRemoved(int position):单参,作用和注意事项与 notifyItemInserted 类似,不过一个是增,一个是减。
  • notifyItemMoved(int fromPosition, int toPosition):双参,作用和 notifyItemRemoved、notifyItemInserted 类似,不过此方法对应的含义是移动

第 2 组

  • notifyItemRangeRemoved(int positionStart, int itemCount):通知范围内的 item 被整体删除了,需要和 notifyItemRangeChanged(int positionStart, int itemCount) 配合使用。
  • notifyItemRangeInserted(int positionStart, int itemCount):通知范围内的 item 整体增加,需要和 notifyItemRangeChanged(int positionStart, int itemCount) 配合使用。
  • notifyItemRangeChanged(int positionStart, int itemCount):通知范围内的 item 变化了

第 3 组

形如 notifyItemChanged(int position, @Nullable Object payload) 等带上 payload 的方法,其含义都是局部刷新。与三参 onBindViewHolder 结合使用。

遇到的坑

  1. Android 中没有提供指定某些 position (位置并不连续) 的 item 更新的方法,我们能做的就是在绑定时进行过滤。
  2. 部分情况下,可能局部通知刷新并不生效,比如 activity 不在前台,并且不在栈顶时。这时候就需要调用整体刷新的方法,如 notifyDataSetChanged 和 notifyItemChanged。
  3. 整体刷新方法部分情况下也有可能并不生效。可能的原因有通知刷新的线程不在 UI 线程(网上的回答,真实性待验证),或者比如 activity 处在后台,处于前台的界面通知其刷新。调用了 notifyItemChanged 方法,此时并不能走到 onBindViewHolder 中,导致界面无法刷新。
  4. notifyDataSetChanged 方法部分情况下可能也不会生效。比如 activity 处于较深的栈底,处于栈顶的界面通知其刷新,此时调用了 notifyDataSetChanged 方法,但是 adapter 并没有走到 onBindViewHolder 方法中。此时就需要将更新的数据记录下来,放入内存或者本地存储中。在界面从栈顶退回栈底 activity 时,便可以读取数据,重走绑定流程,保证数据准确。强烈建议将数据存入本地存储中,这是最稳妥的方式。
  5. 在 onBindViewHolder 中,每次绑定视图时,都应考虑 item 复用的问题。双参的绑定方法不是局部刷新,应视为 item 的整体刷新。在绑定时,应该做到每一个 if 都应该有 else 分支。或者在开始绑定前重置每个视图的状态,如清空 TextView 的文本、清空 ImageView 的图像等等,需要额外注意的一点是,点击事件也需要做清空处理(本人遇到过 item 的点击事件复用导致的 BUG,排查了两天,最终确定的)。
  • 如果因为业务需要,RecyclerView 的 Adapter 需要包含多个子 Adapter,子 Adapter 中又各自定义了多 viewType(即多 Adapter 和多 viewType 混用),则此种实现方式维护起来十分困难,多个 Adapter 混用时,需要维护多个数据列表(data list)。bind 时,父 Adapter 的列表 postion 需要有个转换映射的过程,增加耗时,数据量躲起来时,容易造成卡顿。多 Adapter 和多 viewType 混用的方式不推荐使用,要么多 Adapter,并使用 ConcatAdapter 连接;要么多 viewType,直接使用一个 Adapter。
posted @ 2020-04-27 21:48  尛惢刕孨  阅读(7158)  评论(0编辑  收藏  举报