RecyclerView调用notifyDataSetChanged后需要滚动一下才刷新数据的问题

原代码如下:

JSONObject ret = new JSONObject(event.info());
mWifiScanResult = new JSONArray(ret.getString("data"));
mRecyclerView.getAdapter().notifyDataSetChanged();

其中mWifiScanResult为RecyclerView的数据源,为JSON格式。然而在Android模拟器上执行后并没有刷新数据,需要手动滚动一下才刷新数据。

解决方法:

不能修改完数据就马上调用notifyDataSetChanged(),而是应该延迟一下,如:

JSONObject ret = new JSONObject(event.info());
mWifiScanResult = new JSONArray(ret.getString("data"));
myHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        mRecyclerView.getAdapter().notifyDataSetChanged();
    }
}, 500);

问题完美解决。但到底是什么原理呢?

<think>嗯,用户问的是关于Android中RecyclerView的Adapter调用notifyDataSetChanged时列表会闪烁的问题。这个问题我之前也遇到过,得仔细想想怎么解决。 首先,得了解为什么调用notifyDataSetChanged会导致闪烁。可能的原因是每次调用这个方法时,整个列表项都会被重新绑定,导致视图重新绘制,如果数据变化较大,就会有明显的闪烁效果。这时候需要找替代方法,或者优化数据更新的方式。 然后,想到RecyclerView的Adapter有其他更细粒度的通知方法,比如notifyItemChanged、notifyItemInserted等。这些方法只更新特定的位置,而不是整个列表,应该可以减少闪烁。用户可能是在全部数据更新时用了全局刷新,换成局部更新可能会好很多。 另外,检查一下数据更新的方式是否正确。如果在更新数据时,没有保持数据集的稳定性,比如每次都创建新的列表,可能会导致Adapter认为整个数据都变了。这时候应该保持数据集的引用不变,只更新里面的内容,或者使用DiffUtil来处理差异更新。 DiffUtil是Android提供的工具类,可以计算两个数据集的差异,并自动调用合适的notify方法。这样既能提高效率,又能避免不必要的刷新。比如用DiffUtil.Callback来比较新旧数据,然后通过DiffUtil.DispatchResultToAdapter来应用更新。 还有,动画的问题也可能导致闪烁。默认情况下,RecyclerView的项可能有变更动画,可以尝试关闭默认的动画,看看是否有效。比如设置itemAnimator为null,或者在更新数据时暂时禁用动画。 另外,检查是否有多个地方同时调用notifyDataSetChanged,或者数据被频繁更新,导致界面不断刷新。这种情况下需要优化数据更新的频率,合并多次更新,或者使用防抖机制。 还有一种情况是数据绑定时的布局问题。如果在onBindViewHolder中某些视图的状态没有正确重置,比如图片加载时的占位符,可能会导致短暂闪烁。确保在绑定数据时,所有视图都正确设置了新的数据,并清除旧的状态。 总结一下,可能的解决方案包括使用局部刷新、DiffUtil、关闭动画、优化数据更新方式等。需要根据具体情况选择合适的办法。</think>在 RecyclerView 中使用 `notifyDataSetChanged()` 导致列表闪烁的常见原因及解决方案: 1. **根本原因分析** - `notifyDataSetChanged()` 会强制所有 Item 重新绑定数据 - 导致视图层完全重建,失去原有动画和状态保持能力 - 当数据变化较大时会产生视觉闪烁 2. **推荐解决方案** **方案一:使用 DiffUtil(最佳实践)** ```kotlin class MyDiffCallback( private val oldList: List<Item>, private val newList: List<Item> ) : DiffUtil.Callback() { override fun getOldListSize() = oldList.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean { return oldList[oldPos].id == newList[newPos].id } override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean { return oldList[oldPos] == newList[newPos] } } // 使用示例 val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList)) adapter.submitNewList(newList) // 需要自行实现列表更新方法 diffResult.dispatchUpdatesTo(adapter) ``` **方案二:局部刷新(针对特定修改)** ```kotlin // 插入新项 adapter.notifyItemInserted(position) // 删除项 adapter.notifyItemRemoved(position) // 更新单个项 adapter.notifyItemChanged(position) // 批量更新 adapter.notifyItemRangeChanged(startPos, itemCount) ``` 3. **优化技巧** - **保持数据引用**:不要每次创建新列表,应保持列表对象引用不变 ```kotlin // 推荐做法 originalList.clear() originalList.addAll(newData) ``` - **关闭默认动画**(临时方案) ```kotlin (recyclerView.itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false // 或完全禁用 recyclerView.itemAnimator = null ``` - **稳定ID设置** ```kotlin override fun getItemId(position: Int): Long { return dataList[position].id // 确保每个item有唯一稳定ID } // 在Adapter构造函数中设置 setHasStableIds(true) ``` 4. **常见误区** - ❌ 每次更新都创建新列表: ```kotlin // 错误做法 adapter.list = newList.toMutableList() adapter.notifyDataSetChanged() ``` - ✅ 正确做法: ```kotlin with(adapter.list) { clear() addAll(newList) } // 配合DiffUtil或局部刷新使用 ``` 5. **性能对比** | 方法 | 时间复杂度 | 动画保留 | 闪烁风险 | |---------------------|-------------|----------|----------| | `notifyDataSetChanged` | O(N) | ❌ | 高 | | `DiffUtil` | O(N log N) | ✅ | 低 | | 局部刷新 | O(1) | ✅ | 无 | 建议优先使用 DiffUtil 配合 ListAdapter 实现自动差异计算: ```kotlin class MyAdapter : ListAdapter<Item, ViewHolder>(MyDiffCallback()) { // 自动处理列表更新 fun submitNewList(list: List<Item>) { submitList(list.toMutableList()) } } ``` 通过上述优化,可以消除列表闪烁问题,同时提升滚动流畅度和内存效率。实际项目中推荐结合 Data Binding 或 View Binding 使用,进一步优化视图绑定性能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值