DiffUtil.dispatchUpdatesTo无法更新RecyclerView问题求助
问题:首次使用DiffUtil更新RecyclerView无响应,仅第二次更新生效
我用DiffUtil实现RecyclerView列表更新,但首次向Adapter传入列表时,RecyclerView未收到更新通知,只有第二次更新列表时才会触发通知。试过多个同类问题的解决方案均无效,恳请帮忙。以下是我的代码:
Adapter代码
class UserListAdapter : Adapter<UserListItemViewHolder>() { private var mUsers = mutableListOf<User>() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListItemViewHolder = UserListItemViewHolder.create(parent) override fun onBindViewHolder(holder: UserListItemViewHolder, position: Int) { holder.bind(mUsers[position]) } override fun getItemCount(): Int = mUsers.size fun updateUsersList(users: List<User>) { val diffCallback = UserListDiffCallback(this.mUsers, users) val diffResult = DiffUtil.calculateDiff(diffCallback) this.mUsers.clear() this.mUsers.addAll(users) diffResult.dispatchUpdatesTo(this) } }
UserListDiffCallback代码
class UserListDiffCallback( private val oldList: List<User>, private val newList: List<User> ) : DiffUtil.Callback() { override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldList[oldItemPosition].username == newList[newItemPosition].username } override fun getOldListSize(): Int { return oldList.size } override fun getNewListSize(): Int { return newList.size } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return true } }
ViewModel代码
@HiltViewModel class MainViewModel @Inject constructor( private val getUsersUseCase: GetUsersUseCase ) : ViewModel() { private val _usersResultStatus = MutableLiveData<ResultStatus>(NotLoading) val usersResultStatus: LiveData<ResultStatus> get() = _usersResultStatus private val _users = MutableLiveData<MutableList<User>?>() val users: LiveData<MutableList<User>?> get() = _users fun getUsers() { viewModelScope.launch { try { _usersResultStatus.value = Loading _users.value = getUsersUseCase() } catch (error: Throwable) { _usersResultStatus.value = Error(error) } finally { _usersResultStatus.value = NotLoading } } } }
Activity更新列表代码
@AndroidEntryPoint class MainActivity : AppCompatActivity() { private var _binding: ActivityMainBinding? = null private val binding: ActivityMainBinding get() = _binding!! private val viewModel: MainViewModel by viewModels() private val usersAdapter = UserListAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) _binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) initUsersAdapter() observeStates() viewModel.getUsers() } private fun initUsersAdapter() { with(binding.recyclerView) { setHasFixedSize(true) adapter = usersAdapter } } private fun observeStates() { viewModel.usersResultStatus.observe(this) { resultStatus -> setProgressbarVisibility(resultStatus is Loading) if (resultStatus is Error) { Toast.makeText( this@MainActivity, getString(R.string.error), Toast.LENGTH_SHORT ).show() } } viewModel.users.observe(this) { users -> users?.let { usersAdapter.updateUsersList(users.toMutableList()) } } } private fun setProgressbarVisibility(show: Boolean) { binding.progressBar.visibility = if (show) View.VISIBLE else View.GONE } }
解决方案
1. 为RecyclerView添加LayoutManager
RecyclerView必须设置LayoutManager才能正常渲染列表,你的代码中缺少这一步,导致首次加载数据后无法显示。修改initUsersAdapter方法:
private fun initUsersAdapter() { with(binding.recyclerView) { setHasFixedSize(true) layoutManager = LinearLayoutManager(this@MainActivity) // 添加此行,可根据需求替换为GridLayoutManager等 adapter = usersAdapter } }
2. 修复DiffUtil.Callback的内容对比逻辑
areContentsTheSame直接返回true会导致即使User的内容发生变化,RecyclerView也不会更新。需要对比User的具体字段:
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldUser = oldList[oldItemPosition] val newUser = newList[newItemPosition] // 对比User的所有需要监听变化的字段,示例如下: return oldUser.username == newUser.username && oldUser.age == newUser.age && oldUser.avatarUrl == newUser.avatarUrl }
3. 优化ViewModel中的数据类型(可选)
将_users的类型改为LiveData<List<User>>而非可变列表,避免外部直接修改数据源,提升安全性:
private val _users = MutableLiveData<List<User>>() val users: LiveData<List<User>> = _users // 在getUsers方法中: _users.value = getUsersUseCase() // 确保useCase返回List<User>类型
对应的Activity中观察代码可简化为:
viewModel.users.observe(this) { users -> usersAdapter.updateUsersList(users) }
4. 验证数据源正确性(排查用)
在updateUsersList方法中添加日志,确认首次传入的列表是否非空:
fun updateUsersList(users: List<User>) { Log.d("UserListAdapter", "Received users: ${users.size}") // 原有逻辑... }
内容的提问来源于stack exchange,提问作者Thelgon
相关产品推荐
相关产品推荐

