使用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
相关产品推荐
相关产品推荐

