从 RecyclerView 适配器内访问 ViewModel 函数

发布于 2025-01-14 00:52:54 字数 4041 浏览 0 评论 0原文

我是 Android 开发新手,正在通过一些基本的 Android 应用程序来了解更多信息。我想尽早学习 MVMM 架构最佳实践,并且很好奇如何最好地解决以下任务:在一个简单的列表应用程序中,我的 ViewModel 保存列表数据以及添加和删除数据的功能(这是我从 Android Developer 那里学到的)教程)。

RecyclerView 在 Fragment 中显示列表数据。我使用 RecyclerView 适配器作为用户交互的 UI 列表项和相应的 ViewModel 函数调用之间的“桥梁”。最初,我使用接口实现了这一点,但这不允许我将 ViewModel 的引用作为构造函数参数传递,因此现在我使用内部类,如许多有关在 RecyclerView 适配器中设置 ClickListeners 的帖子中所述

class MainAdapter(
    val viewModel: ListViewModel, // reference to entire ViewModel just to access functions therein
    private var list: LiveData<MutableList<String>>, // stored in ViewModel
    private val listener: OnItemClickListener // defined in inner class below
) :
    RecyclerView.Adapter<MainAdapter.ViewHolder>() {

    inner class OnItemClickListener(viewModel: ListViewModel) { 
        fun onClick(mainItem: String) {
            // (TODO) navigate to DetailListFragment and display appropriate list
        }

        fun onLongClick(mainItem: String) {
            // contact viewModel to delete this item
            viewModel.deleteItemMainList(mainItem)
        }
    }

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view)

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.list_item_text.text = list.value?.elementAt(position)
        holder.itemView.setOnClickListener(View.OnClickListener {
            listener.onClick(list.value?.elementAt(position)!!)
        })
        holder.itemView.setOnClickListener(View.OnClickListener {
            listener.onLongClick(list.value?.elementAt(position)!!)
        })
    }

    override fun getItemCount(): Int {
        return list.value?.size ?: 0
    }

}

:比传递对整个 ViewModel 的引用只是为了访问其中的函数更好的解决方案?

如果这是最合适的解决方案,那么我在片段类中初始化适配器时遇到错误:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        ...

        // Error: 
        // Constructor of inner class OnItemClickListener can be called only with receiver of containing class: 
        adapter = MainAdapter(viewModel ,viewModel.mainList, MainAdapter.OnItemClickListener(viewModel) )
        
        main_list_view.adapter = adapter
        main_list_view.layoutManager = LinearLayoutManager(requireContext())
        recyclerView = binding.mainListView
    }

My ViewModel contains data and functions:

class ListViewModel : ViewModel() {
    private val _mainList = MutableLiveData<MutableList<String>>()
    val mainList: LiveData<MutableList<String>> = _mainList

    init {
        _mainList.value = arrayListOf()
        _detailList.value = arrayListOf(arrayListOf())
    }

    fun addItemMainList(item: String) {
        if (_mainList.value?.contains(item) == false) {
            _mainList.value?.add(item)
            _mainList.value = _mainList.value
            }
    }

    fun deleteItemMainList(item: String) {
        _mainList.value?.remove(item)
        _mainList.value = _mainList.value
    }
}

Lastly,我意识到以这种方式存储列表数据不是持久的,而 Room 是持久保存数据的适当方法,但首先我想了解如何使用 MVMM 最佳实践正确连接 UI 事件和我的 ViewModel。

迄今为止探索的参考示例:

如何在我的应用程序中访问共享 viewModel回收器适配器

RecyclerView 项目点击侦听器的正确方式

https://discuss.kotlinlang.org/t/kotlin-constructor-of-inner-class-nested-can-be-known-only-with-receiver-of-containing-class/7700< /a>

I am new to Android development and working through some basic Android apps to learn more. I want to learn MVMM architecture best practices as early as possible and am curious how to best solve the following task: In a simple list app, my ViewModel holds list data and also functions for adding and deleting the data (this I learned from Android Developer tutorials).

A RecyclerView displays the list data in a Fragment. I am using the RecyclerView adapter as a 'bridge' between the UI list item a user interacts with and the appropriate ViewModel function call. Initially, I implemented this using an interface but this would not allow me to pass a reference to the ViewModel as a constructor parameter so now I am using an inner class as described in many SO posts regarding setting ClickListeners in a RecyclerView adapter:

class MainAdapter(
    val viewModel: ListViewModel, // reference to entire ViewModel just to access functions therein
    private var list: LiveData<MutableList<String>>, // stored in ViewModel
    private val listener: OnItemClickListener // defined in inner class below
) :
    RecyclerView.Adapter<MainAdapter.ViewHolder>() {

    inner class OnItemClickListener(viewModel: ListViewModel) { 
        fun onClick(mainItem: String) {
            // (TODO) navigate to DetailListFragment and display appropriate list
        }

        fun onLongClick(mainItem: String) {
            // contact viewModel to delete this item
            viewModel.deleteItemMainList(mainItem)
        }
    }

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view)

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.list_item_text.text = list.value?.elementAt(position)
        holder.itemView.setOnClickListener(View.OnClickListener {
            listener.onClick(list.value?.elementAt(position)!!)
        })
        holder.itemView.setOnClickListener(View.OnClickListener {
            listener.onLongClick(list.value?.elementAt(position)!!)
        })
    }

    override fun getItemCount(): Int {
        return list.value?.size ?: 0
    }

}

Is there a better solution than passing a reference to my entire ViewModel only to access the functions therein?

If this is the most appropriate solution, I am encountering an error when initializing the adapter within the fragment class:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        ...

        // Error: 
        // Constructor of inner class OnItemClickListener can be called only with receiver of containing class: 
        adapter = MainAdapter(viewModel ,viewModel.mainList, MainAdapter.OnItemClickListener(viewModel) )
        
        main_list_view.adapter = adapter
        main_list_view.layoutManager = LinearLayoutManager(requireContext())
        recyclerView = binding.mainListView
    }

My ViewModel containing data and functions:

class ListViewModel : ViewModel() {
    private val _mainList = MutableLiveData<MutableList<String>>()
    val mainList: LiveData<MutableList<String>> = _mainList

    init {
        _mainList.value = arrayListOf()
        _detailList.value = arrayListOf(arrayListOf())
    }

    fun addItemMainList(item: String) {
        if (_mainList.value?.contains(item) == false) {
            _mainList.value?.add(item)
            _mainList.value = _mainList.value
            }
    }

    fun deleteItemMainList(item: String) {
        _mainList.value?.remove(item)
        _mainList.value = _mainList.value
    }
}

Lastly, i realize storing list data this way is not persistent and Room is the appropriate way to persist data, but first I want to understand how to correctly connect UI events and my ViewModel using MVMM best practices.

Sample of references explored thus far:

How to acces shared viewModel in my recyclerAdapter

RecyclerView Item Click Listener the Right Way

https://discuss.kotlinlang.org/t/kotlin-constructor-of-inner-class-nested-can-be-called-only-with-receiver-of-containing-class/7700

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

哭了丶谁疼 2025-01-21 00:52:54

StackOverflow 关于回收器视图和 Viewmodel 的回答。来自您共享的资源之一的评论回答了您的问题。只需传递必要的内容(例如列表),然后在 recyclerView 中创建接口即可在调用活动或 Fragment 中进行调用。然后从那里访问您的视图模型。

可以将 ViewModel 传递给 recyclerView 吗?...我的意思是回收器视图将与您从回收器视图调用它的片段或活动的生命周期相关联,所以可以,但不是一个好的做法

StackOverflow answer on recycler view and Viewmodels. This comment from one of the resources you shared answers your question. Just pass only what is necessary ( for example a list ) then create interfaces in your recyclerView to get called in the calling activity or Fragment . Then access your Viewmodel from there.

Is it okay to pass ViewModel to recyclerView?.. I mean the recycler view will be tied to the lifecycle of the fragment or activity you call it from recycler view so it's okay but not a good practice

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文