Android RecyclerView Adapter调用notifyItemChanged未触发onBindViewHolder
问题与解决方案
问题描述
实现逻辑:从Firestore加载电影数据到RecyclerView,同时显示加载动画,待图片下载完成后更新Adapter展示电影海报。当前遇到两个问题:
- 调用
notifyItemChanged(x)前已完成movieBitmap赋值,但无法传递到Adapter; onBindViewHolder函数完全未执行。
问题代码
从存储加载图片代码
private fun loadRestMoviesFromStorage() { CoroutineScope(Dispatchers.IO).launch { for (restMoviePosition in 1..movies[currentPosition - 1].restMoviesCount!!) { val file: File = withContext(Dispatchers.IO) { File.createTempFile("restMovieName1", "jpg") } var originalBitMap: Bitmap restMoviesStorageReference .child("restMovies/${restMovie.originalMovieName} Rest$restMoviePosition") .getFile(file) .addOnCompleteListener { if (it.isSuccessful) { originalBitMap = BitmapFactory.decodeFile(file.absolutePath) when (restMoviePosition) { 1 -> restMoviePics[0] = originalBitMap 2 -> restMoviePics[1] = originalBitMap 3 -> restMoviePics[2] = originalBitMap 4 -> restMoviePics[3] = originalBitMap 5 -> restMoviePics[4] = originalBitMap 6 -> restMoviePics[5] = originalBitMap 7 -> restMoviePics[6] = originalBitMap 8 -> restMoviePics[7] = originalBitMap 9 -> restMoviePics[8] = originalBitMap } updateRestMovies(restMoviePosition) } else { handleLoadingErrors("loadRestMoviesFromStorage", it.exception?.message.toString()) } } } } }
初始化RecyclerView Adapter代码
private fun openRestMoviesDialog() { val adapter = movies[currentPosition - 1].restMoviesCount?.let { MovieSeriesAdapter(restMovie, it, restMoviePics) } lifecycleScope.launchWhenResumed { val restDialogTrailerBinding = SeriesMoviesBinding.inflate(layoutInflater) if (seriesTextClicked) { restMovieLoadingDialog.setContentView(restDialogTrailerBinding.root) restDialogTrailerBinding.rvSeriesMovies.adapter = adapter restDialogTrailerBinding.rvSeriesMovies.layoutManager = LinearLayoutManager(requireContext()) } } }
更新函数代码
private fun updateRestMovies(movieNumber: Int) { lifecycleScope.launchWhenResumed { val restDialogTrailerBinding = SeriesMoviesBinding.inflate(layoutInflater) Log.e("a7a", "${restMoviePics[0]}") restDialogTrailerBinding.rvSeriesMovies.adapter?.notifyItemChanged(movieNumber + 1) } }
问题分析
- Binding与Adapter实例不统一:
openRestMoviesDialog中创建的Binding和Adapter是绑定到对话框的实例,但updateRestMovies里重新inflate了新的SeriesMoviesBinding,这个新Binding对应的RecyclerView和实际显示的不是同一个,导致notifyItemChanged完全没作用到目标Adapter。 - 索引计算错误:
restMoviePosition从1开始,但RecyclerView的position从0开始,movieNumber +1会导致索引越界,无法触发正确位置的更新。 - 协程使用不当:使用
CoroutineScope(Dispatchers.IO).launch没有绑定生命周期,存在内存泄漏风险;同时Firebase的异步回调本身在主线程执行,无需额外套IO协程。 - onBindViewHolder未执行:由于每次更新都创建新Binding,实际显示的RecyclerView可能未正确关联Adapter,或者Adapter的itemCount计算错误,导致RecyclerView没有可绑定的项。
修复方案
1. 保存全局Binding与Adapter实例
将Binding和Adapter设为类成员变量,确保所有操作都针对同一个实例:
// 类成员变量 private lateinit var restDialogBinding: SeriesMoviesBinding private var restMoviesAdapter: MovieSeriesAdapter? = null
2. 重构初始化逻辑
修改openRestMoviesDialog,直接绑定全局实例并启动加载任务:
private fun openRestMoviesDialog() { val restCount = movies[currentPosition - 1].restMoviesCount ?: return // 创建Adapter并保存 restMoviesAdapter = MovieSeriesAdapter(restMovie, restCount, restMoviePics) // 初始化Binding并设置到对话框 restDialogBinding = SeriesMoviesBinding.inflate(layoutInflater) if (seriesTextClicked) { restMovieLoadingDialog.setContentView(restDialogBinding.root) restDialogBinding.rvSeriesMovies.apply { adapter = restMoviesAdapter layoutManager = LinearLayoutManager(requireContext()) } // 启动图片加载 loadRestMoviesFromStorage() } }
3. 优化图片加载与更新逻辑
删除updateRestMovies函数,直接在下载回调中更新Adapter,同时修正索引计算:
private fun loadRestMoviesFromStorage() { val restCount = movies[currentPosition - 1].restMoviesCount ?: return // 使用lifecycleScope绑定生命周期,避免内存泄漏 lifecycleScope.launch { for (restMoviePosition in 1..restCount) { val file = withContext(Dispatchers.IO) { File.createTempFile("restMovieName1", "jpg") } restMoviesStorageReference .child("restMovies/${restMovie.originalMovieName} Rest$restMoviePosition") .getFile(file) .addOnCompleteListener { task -> if (task.isSuccessful) { val originalBitMap = BitmapFactory.decodeFile(file.absolutePath) // 修正索引:从1转成0开始的索引 val targetIndex = restMoviePosition - 1 if (targetIndex in restMoviePics.indices) { restMoviePics[targetIndex] = originalBitMap // 直接调用保存的Adapter实例更新对应位置 restMoviesAdapter?.notifyItemChanged(targetIndex) } } else { handleLoadingErrors("loadRestMoviesFromStorage", task.exception?.message.toString()) } } } } }
关键说明
- 确保所有操作都针对同一个Binding和Adapter实例,是解决更新无效的核心;
- 索引必须从0开始对应RecyclerView的position,避免越界或更新错误位置;
- 使用
lifecycleScope管理协程,自动跟随组件生命周期销毁任务,防止内存泄漏; - 简化
restMoviePics的赋值逻辑,直接通过索引计算替代冗余的when分支。
内容的提问来源于stack exchange,提问作者Mark George
相关产品推荐
相关产品推荐

