Kotlin技术问题:如何从SharedViewModel向Adapter传递数据
核心原则
Adapter的职责是仅负责UI渲染和用户交互的分发,不应该直接持有ViewModel引用或访问ViewModel数据。正确的做法是由宿主Fragment/Activity将ViewModel的数据传递给Adapter,或者通过回调让Fragment处理ViewModel相关操作。
方法一:直接传递ViewModel数据给Adapter
将ViewModel中的数据作为参数传入Adapter,若数据是LiveData则观察其变化并更新Adapter:
1. 在Fragment中初始化Adapter并传递数据
class DeactivatedJumpElementsFragment : Fragment(R.layout.deactivated_jump_elements_fragment) { private lateinit var sharedViewModel: DeactivatedElementsViewModel private lateinit var myAdapter: MyAdapter override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 获取共享ViewModel(注意用requireActivity()确保跨Fragment共享) sharedViewModel = ViewModelProvider(requireActivity())[DeactivatedElementsViewModel::class.java] // 初始化Adapter并传入初始数据 val initialData = sharedViewModel.elementsFromStand myAdapter = MyAdapter(initialData) yourRecyclerView.adapter = myAdapter // 若数据是LiveData,观察变化并更新Adapter sharedViewModel.elementsFromStandLiveData.observe(viewLifecycleOwner) { newData -> myAdapter.updateData(newData) } } }
2. 在Adapter中持有数据并提供更新方法
class MyAdapter(private var elementsData: Map<String, Array<Any>>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { // 提供数据更新方法,可配合DiffUtil实现高效刷新 fun updateData(newData: Map<String, Array<Any>>) { elementsData = newData notifyDataSetChanged() } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // 直接使用已传递的elementsData渲染UI val entry = elementsData.entries.elementAt(position) holder.itemText.text = entry.key // ...其他UI操作 } class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val itemText: TextView = itemView.findViewById(R.id.item_text) // ...其他视图引用 } }
方法二:通过回调让Fragment处理ViewModel操作
如果Adapter需要触发ViewModel的修改操作(比如点击按钮更新数据),定义回调接口让Fragment处理:
1. 定义回调接口
interface ElementActionCallback { fun onElementButtonClicked(elementKey: String) }
2. Adapter接收回调并分发事件
class MyAdapter( private val elementsData: Map<String, Array<Any>>, private val callback: ElementActionCallback ) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { override fun onBindViewHolder(holder: ViewHolder, position: Int) { val entry = elementsData.entries.elementAt(position) holder.actionButton.setOnClickListener { // 将点击事件分发给Fragment处理 callback.onElementButtonClicked(entry.key) } } class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val actionButton: Button = itemView.findViewById(R.id.action_button) // ...其他视图引用 } }
3. Fragment实现回调并操作ViewModel
class DeactivatedJumpElementsFragment : Fragment(R.layout.deactivated_jump_elements_fragment), ElementActionCallback { private lateinit var sharedViewModel: DeactivatedElementsViewModel override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedViewModel = ViewModelProvider(requireActivity())[DeactivatedElementsViewModel::class.java] val initialData = sharedViewModel.elementsFromStand myAdapter = MyAdapter(initialData, this) yourRecyclerView.adapter = myAdapter } override fun onElementButtonClicked(elementKey: String) { // 在Fragment中操作ViewModel sharedViewModel.updateElementStatus(elementKey) } }
为什么你的之前方法失败?
- 直接在Adapter中实例化ViewModel:Adapter没有关联的LifecycleOwner(Fragment/Activity),ViewModel需要依附于宿主的生命周期,因此会报"Can't access ViewModels from detached fragment"错误。
- 创建新的Fragment实例获取数据:
DeactivatedJumpElementsFragment()是一个全新的、未初始化的Fragment实例,和当前显示的Fragment不是同一个对象,其ViewModel自然未初始化。
内容的提问来源于stack exchange,提问作者Malte Ge
相关产品推荐
相关产品推荐

