I want to call my ViewModel.delete (Room Database) method from my Recyclerview Adapter but it's not working. Any ideas?
I want to call the ViewModel in OnBindViewHolder like: holder.binding.ivItemWalletDelete.setOnClickListener { WalletViewModel... }
but I get the error: on a null object
RecyclerView Adapter (WalletListAdapter.kt)
class WalletListAdapter:
ListAdapter<Wallet, WalletListAdapter.ViewHolder>(WalletDiffCallback()){
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item)
}
class ViewHolder private constructor(val binding: ItemWalletsBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Wallet) {
binding.wallet = item
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemWalletsBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
}
class WalletDiffCallback : DiffUtil.ItemCallback<Wallet>() {
override fun areItemsTheSame(oldItem: Wallet, newItem: Wallet): Boolean {
return oldItem.walletId == newItem.walletId
}
override fun areContentsTheSame(oldItem: Wallet, newItem: Wallet): Boolean {
return oldItem == newItem
}
}
ViewModel (WalletViewModel.kt)
class WalletViewModel(application: Application): AndroidViewModel(application) {
private val repository = WalletRepository(application)
private val liveWalletList = repository.getLiveDataWallets()
fun insert(wallet: Wallet){
viewModelScope.launch {
repository.insert(wallet)
}
}
fun update(wallet: Wallet){
viewModelScope.launch {
repository.update(wallet)
}
}
fun delete(wallet: Wallet){
viewModelScope.launch {
repository.delete(wallet)
}
}
fun getWalletById(walletId: Long): Wallet? {
var wallet: Wallet? = null
viewModelScope.launch {
wallet = repository.getWalletById(walletId)
}
return wallet
}
fun getAllWallets(): List<Wallet>? {
var wallets: List<Wallet>? = null
viewModelScope.launch {
wallets = repository.getAllWallets()
}
return wallets
}
fun getLiveWalletList(): LiveData<List<Wallet>> = liveWalletList
}
A common way of tackling this kind of problem would be to create a callback that would be passed into the adapter.
In your WalletListAdapter
class pass in a callback that takes a Wallet
object.
class WalletListAdapter(private val onDeleteCallback: (Wallet) -> Unit) {}
In your ViewHolder
you can set an OnClickListener
on each recycler view item and pass in the Wallet
object to the callback function. That would look something like this:
binding.root.setOnClickListener { onDeleteCallback(item) }
Finally, the view (activity, fragment, etc) that initialized the WalletListAdapter
would pass in a function that accepts a Wallet
object. This is where you then call the WalletViewModel
's delete function.
class WalletListActivity: Activity() {
@Inject
lateinit var viewModel: WalletViewModel
override fun onCreate(bundle: Bundle?) {
super.onCreate(savedInstanceState)
// initialize the WalletListAdapter and pass in the callback
val adapter = WalletListAdapter(::deleteWallet)
}
private fun deleteWallet(wallet: Wallet) {
viewModel.delete(wallet)
}
}
Note: You can first initialize the WalletListAdapter
class with an empty constructor, then create a method to pass in the callback function from the view to the WalletListAdapter
class. The point is to "propagate" the click event from the adapter back to the view, so the view can call the viewmodel's method.