使用 savestate 保存 recyclerview 项目

Posted

技术标签:

【中文标题】使用 savestate 保存 recyclerview 项目【英文标题】:Saving recyclerview items with savestate 【发布时间】:2021-10-27 05:55:20 【问题描述】:

我目前正在编写一个显示电影列表的应用程序。该应用有 8 个片段包含回收视图:热门电影、动作片、喜剧片、恐怖片、浪漫片、科幻片、搜索和收藏夹。

recyclerview 中的项目包含一个复选框,用于将电影添加到收藏夹。当我滚动或退出应用程序时,复选框状态会重置。我正在尝试使用 savestate 保存复选框的状态,但它不起作用。

谁能告诉我我做错了什么?下面是视图模型。

谢谢。

MoviesListViewModel.kt

package com.example.moviesapp.ui

import androidx.lifecycle.*
import com.example.moviesapp.network.MoviesRepository
import com.example.moviesapp.network.MoviesResults
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

const val DEFAULT_QUERY = " "
const val ACTION_MOVIES = "moviesAction"
const val COMEDY_MOVIES = "moviesComedy"
const val HORROR_MOVIES = "moviesHorror"
const val ROMANCE_MOVIES = "moviesRomance"
const val SCIFI_MOVIES = "moviesScifi"
const val TRENDING_MOVIES = "moviesTrending"


enum class MovieApiStatus LOADING, ERROR, DONE


@HiltViewModel
class MoviesListViewModel @Inject constructor(
    private val repository: MoviesRepository,
  private var state: SavedStateHandle

): ViewModel() 

       private val _moviesAction: MutableLiveData<List<MoviesResults.Movies>> = state.getLiveData(ACTION_MOVIES)
       val moviesAction: LiveData<List<MoviesResults.Movies>> = _moviesAction

       private val _moviesComedy: MutableLiveData<List<MoviesResults.Movies>> = state.getLiveData(COMEDY_MOVIES)
       val moviesComedy: LiveData<List<MoviesResults.Movies>> = _moviesComedy

       private val _moviesHorror: MutableLiveData<List<MoviesResults.Movies>> = state.getLiveData(HORROR_MOVIES)
       val moviesHorror: LiveData<List<MoviesResults.Movies>> = _moviesHorror

       private val _moviesRomance: MutableLiveData<List<MoviesResults.Movies>> = state.getLiveData(
           ROMANCE_MOVIES)
       val moviesRomance: LiveData<List<MoviesResults.Movies>> = _moviesRomance

       private val _moviesScifi: MutableLiveData<List<MoviesResults.Movies>> = state.getLiveData(SCIFI_MOVIES)
       val moviesScifi: LiveData<List<MoviesResults.Movies>> = _moviesScifi

    private val _moviesTrending: MutableLiveData<List<MoviesResults.Movies>> = state.getLiveData(TRENDING_MOVIES)
    val moviesTrending: LiveData<List<MoviesResults.Movies>> = _moviesTrending

    private val _networkState = MutableLiveData<MovieApiStatus>()
    val networkState: LiveData<MovieApiStatus> = _networkState





    init 
        getMovies()

    




 fun getAction() 
     viewModelScope.launch 
         _moviesAction.value = repository.getActionMovies()
     
 

     fun getComedy() 
         viewModelScope.launch 
             _moviesComedy.value = repository.getComedyMovies()

         

     

    fun getHorror() 
        viewModelScope.launch 
            _moviesHorror.value = repository.getHorrorMovies()
        

    
    fun getRomance() 
        viewModelScope.launch 
            _moviesRomance.value = repository.getRomanceMovies()
        

    

    fun getScifi() 
        viewModelScope.launch 
            _moviesScifi.value = repository.getScifiMovies()

        

    

    fun getTrending() 
        viewModelScope.launch 
            _moviesTrending.value = repository.getTrendingMovies()
        

    









    private var currentQuery = MutableLiveData(DEFAULT_QUERY)







     val movies = currentQuery.switchMap 
            queryString ->
       liveData 
           emit(repository.getSearchResults(queryString))
       
   

    fun searchMovies(query: String) 

    currentQuery.value = query

    

    private fun getMovies() 
        viewModelScope. launch 
            _networkState.value = MovieApiStatus.LOADING
            try 
                _networkState.value = MovieApiStatus.DONE
            
            catch (e: Exception) 
                _networkState.value = MovieApiStatus.ERROR

            

        

    



    class MoviesListViewModelFactory @Inject constructor(private val repository: MoviesRepository, private val state: SavedStateHandle): ViewModelProvider.Factory 
        override fun <T : ViewModel?> create(modelClass: Class<T>): T 
            if (modelClass.isAssignableFrom(MoviesListViewModel::class.java)) 
                @Suppress("UNCHECKED_CAST")
                return MoviesListViewModel(repository, state) as T
            
            throw IllegalArgumentException("Unknown ViewModel class")

        


    








【问题讨论】:

您应该存储在SharedPreferences 或类似DataStore 等的类似文件中,因为saveState 仅在活动停止或销毁之前有效 当您退出应用程序时,在 saveState 中保存不会保留数据。保存状态的数据仅在活动旋转或应用程序在后台时才会保留。 SharedPreferences 现已弃用。那我应该用什么? 【参考方案1】:

用于访问和修改 Context.getSharedPreferences(String, int) 返回的首选项数据的接口。对于任何特定的首选项集,所有客户端都共享一个此类的单个实例。对首选项的修改必须通过 Editor 对象,以确保首选项值保持一致状态并控制何时提交到存储。从各种 get 方法返回的对象必须被应用程序视为不可变对象。

注意:这个类提供了强大的一致性保证。它正在使用可能会降低应用程序速度的昂贵操作。频繁更改的属性或可以容忍损失的属性应使用其他机制。有关详细信息,请阅读 Editor.commit()Editor.apply() 上的 cmets。

注意:该类不支持跨多个进程使用。

在您的情况下,您可以简单地存储键值对来记录相关选择。

【讨论】:

共享偏好已被弃用 Jetpack DataStore 是一个不错的选择 我尝试使用 jetpack 数据存储保存复选框的状态,但我无法访问该复选框,因为该复选框位于布局内,并且该布局用于 recyclerview。

以上是关于使用 savestate 保存 recyclerview 项目的主要内容,如果未能解决你的问题,请参考以下文章

Qt - Pyside - .saveGeom() .saveState() (再次)

在 JSF 中,“saveState()”方法被调用了两次。为啥?

在页面生命周期执行时 Page 对象在 SaveState 阶段都发生了什么事?

5.2.6 保存窗口状态

报告实验室保存位置

如何保存和恢复作为音频单元主机的音频单元状态