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

Android RecyclerView Adapter调用notifyItemChanged未触发onBindViewHolder

问题与解决方案

问题描述

实现逻辑:从Firestore加载电影数据到RecyclerView,同时显示加载动画,待图片下载完成后更新Adapter展示电影海报。当前遇到两个问题:

  1. 调用notifyItemChanged(x)前已完成movieBitmap赋值,但无法传递到Adapter;
  2. 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)
    }
}

问题分析

  1. Binding与Adapter实例不统一:openRestMoviesDialog中创建的Binding和Adapter是绑定到对话框的实例,但updateRestMovies里重新inflate了新的SeriesMoviesBinding,这个新Binding对应的RecyclerView和实际显示的不是同一个,导致notifyItemChanged完全没作用到目标Adapter。
  2. 索引计算错误:restMoviePosition从1开始,但RecyclerView的position从0开始,movieNumber +1会导致索引越界,无法触发正确位置的更新。
  3. 协程使用不当:使用CoroutineScope(Dispatchers.IO).launch没有绑定生命周期,存在内存泄漏风险;同时Firebase的异步回调本身在主线程执行,无需额外套IO协程。
  4. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:05:23