Shailesh Kumar
Shailesh Kumar

Reputation: 405

How to use viewmodel in recyclerview adapter

I am creating Recyclerview using MVVM and data binding. Now I need to perform some network operation in Recycler view adapter. So how can we create ViewModel and Live data for adapter. How can adapter observe live data.

I have create ViewModel using activity context and but not working proper

class CartAdapter(cartList: ArrayList<ProductData>, context: BaseActivity) :
    RecyclerView.Adapter<CartAdapter.MyViewHolder>() {

    private val itemList = cartList
    private val activity = context
    private var viewModel: CartAdapterViewModel =
        ViewModelProvider(context).get(CartAdapterViewModel::class.java)

    init {
        initObserver()
    }


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

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = itemList[position]
        holder.setData(item)

        }

    override fun getItemCount(): Int {
        return itemList.size
    }

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var binding: CartItemLayoutBinding? = DataBindingUtil.bind(itemView)


        fun setData(model: ProductData) {
            binding?.item = model

        }
    }

    private fun initObserver() {
        viewModel.statusResponse.observe(activity, {
            activity.hideLoader()
            
        })

        viewModel.serverError.observe(activity, {
            activity.hideLoader()
        })
    }

}

Upvotes: 0

Views: 5329

Answers (2)

sweak
sweak

Reputation: 1970

You should not create a separate ViewModel for adapter.

The Classic way:

The adapter should expose an interface whose implementation would later handle e.g. clicks on an item in the RecyclerView.

class CartAdapter(
    cartList: ArrayList<ProductData>,
    private val itemClickListener: ItemClickListener // This is the interface implementation
    // that will be provided for an item click in this example
) : RecyclerView.Adapter<CartAdapter.MyViewHolder>() {

    interface ItemClickListener {
        fun onItemClick(position: Int)
    }

    override fun onBindViewHolder(holder: ScanResultViewHolder, position: Int) {
        holder.binding.root.setOnClickListener {
            itemClickListener.onItemClick(position)
        }
    }

    // this function would be useful for retrieving an item from the recyclerview
    fun getItemAt(position: Int): ProductData = itemList[position]
    ...
}

Later on when instantiating the CartAdapter in Your Activity or Fragment You would have to provide that interface implementation:

private val cartAdapter: CartAdapter = CartAdapter(
        cartList,
        object : CartAdapter.ItemClickListener {
            override fun onItemClick(position: Int) {
                // this function will handle the item click on a provided position
                doSomethingWithARecyclerViewItemFrom(position)
            }
        }
    )

private fun doSomethingWithARecyclerViewItemFrom(position: Int) {
    // get the adapter item from the position
    val item = cartAdapter.getItemAt(position)
    // later on You can use that item to make something usefull with Your ViewModel of an activity/fragment
    ...
}

This way the Adapter doesn't have to have any ViewModels - the corresponding actions on RecyclerView items can be handled by the Activity view model.

In my example this action is an item click but for a more specific action, You would have to update Your question with those details.

The more compact way:

You can implement the same functionality as above using even more compact and neat way by using function types:

class CartAdapter(
    cartList: ArrayList<ProductData>,
    private val itemClickListener: (productData: ProductData) -> Unit // notice here
) : RecyclerView.Adapter<CartAdapter.MyViewHolder>() {

    override fun onBindViewHolder(holder: ScanResultViewHolder, position: Int) {
        holder.binding.root.setOnClickListener {
            itemClickListener(position) // slight change here also
        }
    }
}

Upvotes: 2

tyranno choco
tyranno choco

Reputation: 91

My suggestion is using constructor to pass viewModel instance. Without concerns of unhandled instance scope problem anyway.

Have a happy day.

Upvotes: 0

Related Questions