Search code examples
androidviewmodelandroid-recyclerview

Proper way to access ViewModel from Recyler Adaper


I have a Data Repository that saved data & fetches data, this is passed into a ViewModel as constructor. I have methods in a ViewModel that fetches and saves data.

I have a button that I click for each row (Item in a RecyclerView list) this saves data using the ViewModel.

I have found that I can directly call a ViewModel initialised it into the constructor, I checked the Google Android examples & this part is not covered.

Something like this below: Copied from: Databinding Recyclerview and onClick

private ExampleViewModel exampleViewModel;

public ExampleListAdapter(Context context, List<Model> models) {
        this.context = context;
        this.models = models;

        // ...
        exampleViewModel = ViewModelProviders.of((FragmentActivity) context).get(ExampleViewModel.class);
}

But then, I could also call a ViewModel by passing a ViewModel object from the Activity alongside with the context.

So what the proper way of calling a ViewModel?


Solution

  • This is your pojo class

    data class Item(val id: Int)
    

    This is your adapter.

    class Adapter : RecyclerView.Adapter<Adapter.ViewHolder>() {
    
        var items: List<Item> = emptyList()
            set(value) {
                field = value
                notifyDataSetChanged()
            }
    
        var callback: Callback? = null
    
        override fun getItemCount(): Int {
            return items.size
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val itemView = LayoutInflater.from(parent.context)
                .inflate(R.layout.simple_textview, parent, false)
            return ViewHolder(itemView)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val item = items[position]
            holder.itemView.setOnClickListener {
                callback?.onItemClicked(item)
            }
            holder.bindItem(item)
        }
    
        inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
            fun bindItem(item: Item) {
                // Fill layout
            }
        }
    
        interface Callback {
            fun onItemClicked(item: Item)
        }
    }
    

    This is your viewModel class.

    class MyViewModel : ViewModel(), Adapter.Callback {
    
        override fun onItemClicked(item: Item) {
    
        }
    }
    

    And this is your fragment.

    class MyFragment : Fragment() {
    
        private val adapter = Adapter()
    
        private lateinit var myViewModel: MyViewModel
    
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            myViewModel = ViewModelProviders.of(activity!!).get(MyViewModel::class.java)
            adapter.callback = myViewModel
        }
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            return inflater.inflate(R.layout.my_fragment, container, false)
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            adapter.items = listOf(
                Item(1),
                Item(2)
            )
    
            //Setup recyclerView etc.
        }
    }