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

Android Kotlin:如何将MovieItem存入SharedPreferences并在收藏Fragment展示

完整电影收藏持久化实现流程

一、持久化方案选择

对于轻量的收藏列表,SharedPreferences是简单高效的选择(如果后续需要复杂查询或大数据量,可切换为Room数据库)。我们将收藏的MovieItem序列化为JSON数组,存入SharedPreferences实现持久化。

二、前置准备:确保可序列化

首先确认Image类也支持Gson序列化(实现Serializable或确保所有字段可被Gson解析):

data class Image(
    @SerializedName("medium") val mediumUrl: String?,
    @SerializedName("original") val originalUrl: String?
): Serializable // 必须实现Serializable,或确保字段都是基本类型/可序列化类型

三、详情页收藏逻辑(DetailViewModel)

实现添加/取消收藏、判断收藏状态的核心逻辑,直接操作SharedPreferences:

class DetailViewModel : ViewModel() {
    private val gson = Gson()
    private val sharedPref = ApplicationProvider.getApplicationContext<Context>()
        .getSharedPreferences("MovieFavorites", Context.MODE_PRIVATE)

    // 添加收藏(自动去重)
    fun addToFavorites(movie: MovieItem) {
        // 读取已有收藏列表
        val existingJson = sharedPref.getString("favorites_list", "[]")
        val favorites = gson.fromJson(existingJson, Array<MovieItem>::class.java).toMutableList()
        
        // 根据id去重,避免重复收藏
        if (!favorites.any { it.id == movie.id }) {
            favorites.add(movie)
            // 保存更新后的列表
            val updatedJson = gson.toJson(favorites)
            sharedPref.edit().putString("favorites_list", updatedJson).apply()
        }
    }

    // 取消收藏
    fun removeFromFavorites(movieId: Int) {
        val existingJson = sharedPref.getString("favorites_list", "[]")
        val favorites = gson.fromJson(existingJson, Array<MovieItem>::class.java).toMutableList()
        favorites.removeAll { it.id == movieId }
        val updatedJson = gson.toJson(favorites)
        sharedPref.edit().putString("favorites_list", updatedJson).apply()
    }

    // 判断当前电影是否已收藏(用于切换详情页按钮状态)
    fun isMovieFavorite(movieId: Int): Boolean {
        val existingJson = sharedPref.getString("favorites_list", "[]")
        val favorites = gson.fromJson(existingJson, Array<MovieItem>::class.java).toList()
        return favorites.any { it.id == movieId }
    }
}

四、收藏列表读取与状态管理(FavoritesViewModel)

用LiveData暴露收藏列表,让Fragment自动感知数据变化:

class FavoritesViewModel : ViewModel() {
    private val gson = Gson()
    private val sharedPref = ApplicationProvider.getApplicationContext<Context>()
        .getSharedPreferences("MovieFavorites", Context.MODE_PRIVATE)
    
    // 对外暴露不可变的LiveData,内部用MutableLiveData更新
    private val _favoritesList = MutableLiveData<List<MovieItem>>()
    val favoritesList: LiveData<List<MovieItem>> = _favoritesList

    // 初始化时加载收藏列表
    init {
        loadFavorites()
    }

    // 加载收藏列表
    fun loadFavorites() {
        val favoritesJson = sharedPref.getString("favorites_list", "[]")
        val favoritesArray = gson.fromJson(favoritesJson, Array<MovieItem>::class.java)
        _favoritesList.value = favoritesArray.toList()
    }

    // 手动刷新列表(比如从详情页返回时调用)
    fun refreshFavorites() {
        loadFavorites()
    }
}

五、收藏Fragment展示逻辑

观察ViewModel的LiveData,自动更新RecyclerView:

class FavoritesFragment : Fragment() {
    private lateinit var viewModel: FavoritesViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var movieAdapter: MovieAdapter // 复用原有的MovieItem适配器

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_favorites, container, false)
        recyclerView = view.findViewById(R.id.recycler_favorites)
        // 初始化适配器,传入空列表
        movieAdapter = MovieAdapter(emptyList())
        recyclerView.apply {
            adapter = movieAdapter
            layoutManager = LinearLayoutManager(context)
        }
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(this)[FavoritesViewModel::class.java]
        
        // 观察收藏列表变化,自动更新UI
        viewModel.favoritesList.observe(viewLifecycleOwner) { movies ->
            movieAdapter.updateData(movies)
        }
    }

    // 回到前台时刷新列表,确保数据最新
    override fun onResume() {
        super.onResume()
        viewModel.refreshFavorites()
    }
}

六、适配器优化

给MovieAdapter添加数据更新方法,推荐用DiffUtil提升性能:

class MovieAdapter(private var movies: List<MovieItem>) : RecyclerView.Adapter<MovieAdapter.MovieViewHolder>() {
    // ... 你的ViewHolder实现和绑定逻辑

    // 简单更新(如果数据量小)
    fun updateData(newMovies: List<MovieItem>) {
        movies = newMovies
        notifyDataSetChanged()
    }

    // 推荐:用DiffUtil优化更新,避免全量刷新
    fun updateDataWithDiff(newMovies: List<MovieItem>) {
        val diffResult = DiffUtil.calculateDiff(MovieDiffCallback(movies, newMovies))
        movies = newMovies
        diffResult.dispatchUpdatesTo(this)
    }

    // DiffCallback实现
    private class MovieDiffCallback(
        private val oldList: List<MovieItem>,
        private val newList: List<MovieItem>
    ) : DiffUtil.Callback() {
        override fun getOldListSize() = oldList.size
        override fun getNewListSize() = newList.size
        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = 
            oldList[oldItemPosition].id == newList[newItemPosition].id
        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = 
            oldList[oldItemPosition] == newList[newItemPosition]
    }
}

七、常见问题修正

  • 你之前的getFavorites方法存在变量重名问题,且错误使用MovieItem.class(Kotlin中应使用MovieItem::class.java)
  • 仅转换JSON而不保存到持久化介质,重启后数据会丢失,必须将JSON写入SharedPreferences或数据库

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:10:19