You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 10:25:22