You need to enable JavaScript to run this app. 如何重构RecyclerView代码接入ViewModel和LiveData实现数据跨重启保存恢复
优惠活动
大模型
产品
解决方案
定价
更多

如何重构RecyclerView代码接入ViewModel和LiveData实现数据跨重启保存恢复

实现步骤

1. 改造RecyclerAdapter支持动态数据更新

原有Adapter传入的是不可变列表,无法后续更新数据,我们调整为可变列表并新增提交数据的方法:

class RecyclerAdapter(private val listener: OnRecyclerClickListener) : RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder>() {
    private var recyclerList = emptyList<RecyclerCard>()

    // 新增提交数据方法,更新列表后刷新UI
    fun submitList(newList: List<RecyclerCard>) {
        recyclerList = newList
        notifyDataSetChanged()
        // 数据量大的话可以替换为DiffUtil提升性能
    }

    // 原有ViewHolder、点击接口、onCreateViewHolder、onBindViewHolder逻辑保持不变
    inner class RecyclerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
        val recyclerImageView: AppCompatImageView =
            itemView.findViewById(R.id.recycler_picture_image_view)
        val recyclerTitleView: AppCompatTextView = itemView.findViewById(R.id.recycler_title_text_view)
        val recyclerScoreView: AppCompatTextView =
            itemView.findViewById(R.id.recycler_score_text_view)

        init {
            itemView.setOnClickListener(this)
        }

        override fun onClick(v: View?) {
            val position = adapterPosition
            if (position != RecyclerView.NO_POSITION) {
                listener.onRecyclerClick(position)
            }
        }
    }

    interface OnRecyclerClickListener {
        fun onRecyclerClick(position: Int)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
        val itemView =
            LayoutInflater.from(parent.context).inflate(R.layout.recycler_card, parent, false)
        return RecyclerViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
        val currentRecycler = recyclerList[position]
        Glide.with(holder.itemView.context)
            .load(currentRecycler.recyclerImage)
            .apply(RequestOptions.skipMemoryCacheOf(true))
            .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE))
            .into(holder.recyclerImageView)
        holder.recyclerTitleView.text = currentRecycler.recyclerTitle
        holder.recyclerScoreView.text = currentRecycler.recyclerScore
    }

    override fun getItemCount(): Int = recyclerList.size
}

2. 给数据实体类添加序列化能力

要持久化存储RecyclerCard数据,需要让它支持序列化,这里以实现Serializable为例:

import java.io.Serializable

data class RecyclerCard(
    val recyclerImage: String,
    val recyclerTitle: String,
    val recyclerScore: String,
    val extra: String // 对应你原来的第四个参数string3
) : Serializable

3. 实现ViewModel+LiveData管理数据

ViewModel负责持有数据、处理逻辑、持久化存储,LiveData负责通知UI更新。这里用SharedPreferences+Gson实现轻量持久化,适合数据量不大的场景:

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainViewModel(application: Application) : AndroidViewModel(application) {
    // 持有的列表数据,LiveData可被UI层观察
    val cardList = MutableLiveData<ArrayList<RecyclerCard>>()
    private val sp = application.getSharedPreferences("recycler_data", 0)
    private val gson = Gson()

    init {
        // 初始化时先读本地存储的缓存数据
        loadCacheData()
    }

    // 读取本地缓存
    private fun loadCacheData() {
        val jsonStr = sp.getString("card_list", null) ?: return
        val type = object : TypeToken<ArrayList<RecyclerCard>>() {}.type
        val cacheList = gson.fromJson<ArrayList<RecyclerCard>>(jsonStr, type)
        cardList.postValue(cacheList)
    }

    // 新增卡片数据,同时更新本地缓存
    fun addCard(card: RecyclerCard) {
        viewModelScope.launch(Dispatchers.IO) {
            val currentList = cardList.value ?: arrayListOf()
            currentList.add(card)
            // 存到本地
            sp.edit()
                .putString("card_list", gson.toJson(currentList))
                .apply()
            // 通知UI更新
            cardList.postValue(currentList)
        }
    }

    // 你如果有删除、修改数据的需求,同样在这里处理并同步更新本地存储即可
}

4. 改造Fragment只做UI交互

Fragment不再持有数据,只负责初始化UI、观察LiveData变化、触发ViewModel的操作:

class Main : Fragment(), RecyclerAdapter.OnRecyclerClickListener {
    private lateinit var mainRecyclerView: RecyclerView
    private lateinit var adapter: RecyclerAdapter
    // 初始化ViewModel,和Fragment生命周期绑定
    private val viewModel by viewModels<MainViewModel>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        mainRecyclerView = view.findViewById(R.id.main_recycler_view)
        // 初始化adapter和RecyclerView
        adapter = RecyclerAdapter(this)
        mainRecyclerView.adapter = adapter
        mainRecyclerView.layoutManager = LinearLayoutManager(requireContext())
        mainRecyclerView.setHasFixedSize(true)

        // 观察ViewModel的列表数据,变化时自动更新adapter
        viewModel.cardList.observe(viewLifecycleOwner) { newList ->
            adapter.submitList(newList)
        }

        // 原来的IO逻辑移到ViewModel中执行,避免内存泄漏
        viewModel.addCard(
            RecyclerCard(
                string1,
                string2,
                score,
                string3
            )
        )
    }

    override fun onRecyclerClick(position: Int) {
        // 原有点击逻辑保持不变
    }
}

补充说明

  • ViewModel本身可以解决配置变更(如屏幕旋转、语言切换)时的数据不丢失问题,搭配SharedPreferences/Room等本地持久化方案才能实现应用彻底关闭后重启仍然恢复数据的需求
  • 如果你的数据量较大、有复杂查询需求,推荐将持久化逻辑替换为Room数据库,性能和稳定性更好
  • 代码中用到Gson需要提前在build.gradle中引入依赖,notifyDataSetChanged可以替换为DiffUtil实现局部刷新,提升列表滑动性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 06:15:02