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

使用SavedState保存RecyclerView列表项复选框状态失效问题求助

问题原因
  • 你目前仅将电影列表数据存入SavedStateHandle,但MoviesResults.Movies实体类未定义收藏状态相关字段,每次从接口拉取原始数据后直接覆盖LiveData的值,之前记录的选中状态会被直接清空。
  • SavedStateHandle仅能在系统主动销毁进程(如后台回收)时暂存数据,完全退出应用的场景下无法持久化保存状态,且你当前并未将收藏状态单独存入SavedStateHandle或本地持久化存储。
  • RecyclerView滚动时状态重置是因为Adapter的onBindViewHolder方法未根据电影实际的收藏状态赋值复选框,仅使用控件默认状态,item滚出屏幕再复用就会回到初始状态。
修复方案

1. 扩展电影实体类

在MoviesResults.Movies中新增收藏状态字段:

data class Movies(
    // 保留原有所有字段(id、标题、海报地址等)
    var isFavorited: Boolean = false
)

2. 统一维护收藏状态

在ViewModel中单独维护收藏的电影ID集合,同时做双份存储:SavedStateHandle处理进程回收场景,本地持久化(SharedPreferences/Room)处理退出应用场景:

// 新增常量
private const val FAVORITE_MOVIES_KEY = "favorite_movie_ids"

@HiltViewModel
class MoviesListViewModel @Inject constructor(
    private val repository: MoviesRepository,
    private var state: SavedStateHandle,
    // 注入SharedPreferences,也可以替换为Room做持久化
    private val sharedPreferences: SharedPreferences
): ViewModel() {
    // 收藏的电影ID集合,同时和SavedState绑定
    private val _favoriteMovieIds: MutableLiveData<Set<Int>> = state.getLiveData(FAVORITE_MOVIES_KEY, emptySet())
    val favoriteMovieIds: LiveData<Set<Int>> = _favoriteMovieIds

    // 切换收藏状态的方法,供Adapter调用
    fun toggleFavorite(movieId: Int) {
        val currentFavorites = _favoriteMovieIds.value ?: emptySet()
        val newFavorites = if (currentFavorites.contains(movieId)) {
            currentFavorites - movieId
        } else {
            currentFavorites + movieId
        }
        _favoriteMovieIds.value = newFavorites
        // 同步存入本地持久化
        sharedPreferences.edit()
            .putStringSet(FAVORITE_MOVIES_KEY, newFavorites.map { it.toString() }.toSet())
            .apply()
    }

    init {
        // 初始化时读取本地存储的收藏状态
        val savedFavorites = sharedPreferences.getStringSet(FAVORITE_MOVIES_KEY, emptySet())
            ?.map { it.toInt() }
            ?.toSet() ?: emptySet()
        _favoriteMovieIds.value = savedFavorites
        getMovies()
    }
    // 原有其他代码保留
}

3. 修改数据拉取逻辑

拉取到接口返回的原始电影数据后,同步注入收藏状态再赋值给LiveData,以热门电影为例,其他分类逻辑完全一致:

fun getTrending() {
    viewModelScope.launch {
        val rawMovies = repository.getTrendingMovies()
        val favoriteIds = _favoriteMovieIds.value ?: emptySet()
        // 给每个电影标记当前收藏状态
        val moviesWithFavorite = rawMovies.map { movie ->
            movie.copy(isFavorited = favoriteIds.contains(movie.id))
        }
        _moviesTrending.value = moviesWithFavorite
    }
}

4. 调整Adapter逻辑

  • 在onBindViewHolder中,根据当前item对应的movie.isFavorited字段设置复选框的选中状态,不要使用控件默认值
  • 复选框的选中变化回调中调用ViewModel的toggleFavorite方法传入当前电影ID,同时更新当前item的isFavorited状态
  • 如果使用ListAdapter,需要在差异计算回调中加入isFavorited字段的比对,避免状态更新时列表闪烁。

内容的提问来源于stack exchange,提问作者Ahmadddd4

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 11:54:03