У меня есть recyclerView, который показывает список элементов корзины. Каждый элемент доступен для клика и открывает фрагмент сведений для этого элемента. Я обновляю макет элемента, чтобы иметь кнопку удаления внутри, кнопка удаления предполагает вызов метода внутри фрагмента viewModel . Я считаю, что создание viewModel в качестве конструктора в адаптере — не лучшая практика, так как разделение ответственности важно, поскольку я развиваю свои навыки.
Я делаю это с помощью dataBinding, и я так много искал и не нашел ответа.
CartListItem.xml
<layout xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
xmlns:tools = "http://schemas.android.com/tools">
<data>
<variable
name = "makeupItem"
type = "com.melfouly.makeupshop.model.MakeupItem" />
<variable
name = "viewModel"
type = "com.melfouly.makeupshop.makeupcart.CartViewModel" />
</data>
<com.google.android.material.card.MaterialCardView
android:id = "@+id/cart_card_item"
android:layout_width = "match_parent"
android:layout_height = "100dp"
android:layout_margin = "4dp"
android:backgroundTint = "@color/primary"
app:cardCornerRadius = "8dp"
app:cardElevation = "8dp">
<LinearLayout
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:weightSum = "6">
<ImageView
android:id = "@+id/item_image"
loadImage = "@{makeupItem}"
android:layout_gravity = "center"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "1"
android:scaleType = "fitCenter"
tools:src = "@tools:sample/avatars" />
<TextView
android:id = "@+id/item_name"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "2"
android:fontFamily = "@font/aclonica"
android:gravity = "center"
android:text = "@{makeupItem.name}"
android:textColor = "@color/black"
android:textStyle = "bold"
tools:text = "Item name" />
<TextView
android:id = "@+id/item_price"
loadPrice = "@{makeupItem}"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "2"
android:fontFamily = "@font/aclonica"
android:gravity = "center"
android:textColor = "@color/black"
tools:text = "5$" />
<Button
android:id = "@+id/delete_button"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "1"
android:onClick = "@{() -> viewModel.deleteItemFromCart(makeupItem)}"
app:icon = "@drawable/ic_baseline_delete_outline_24"
app:iconGravity = "end" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
КорзинаАдаптер
class CartAdapter(private val clickListener: (MakeupItem) -> Unit) :
ListAdapter<MakeupItem, CartAdapter.CartViewHolder>(DiffCallback()) {
class CartViewHolder(private val binding: CartListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(makeupItem: MakeupItem) {
binding.makeupItem = makeupItem
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = CartListItemBinding.inflate(layoutInflater, parent, false)
return CartViewHolder(binding)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
val makeupItem = getItem(position)
holder.itemView.setOnClickListener {
clickListener(makeupItem)
}
holder.bind(makeupItem)
}
class DiffCallback : DiffUtil.ItemCallback<MakeupItem>() {
override fun areItemsTheSame(oldItem: MakeupItem, newItem: MakeupItem): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: MakeupItem, newItem: MakeupItem): Boolean {
return oldItem == newItem
}
}
Функция удаления CartViewModel
fun deleteItemFromCart(makeupItem: MakeupItem) {
viewModelScope.launch {
Log.d(TAG, "DeleteItemFromCart method in viewModel called")
repository.deleteItemFromCart(makeupItem)
}
}
Я пришел к ответу.
DataBinding viewModel с элементом recyclerView не будет работать, поскольку мы не объявляем адаптер в этой модели представления, поэтому вы должны сделать callBack внутри вашего адаптера и получить его в своем фрагменте, а затем вызвать функцию viewModel.
Вот CartAdapter после изменения callBack для вашей кнопки удаления onClick и используйте тот же способ для вашего cardItem
class CartAdapter(
private val cartItemClickListener: CartItemClickListener,
private val deleteItemClickListener: DeleteItemClickListener
) :
ListAdapter<MakeupItem, CartAdapter.CartViewHolder>(DiffCallback()) {
class CartViewHolder(private val binding: CartListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(
makeupItem: MakeupItem,
cartItemClickListener: CartItemClickListener,
deleteItemClickListener: DeleteItemClickListener
) {
binding.makeupItem = makeupItem
binding.cartItemClickListener = cartItemClickListener
binding.deleteItemClickListener = deleteItemClickListener
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = CartListItemBinding.inflate(layoutInflater, parent, false)
return CartViewHolder(binding)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
val makeupItem = getItem(position)
holder.bind(makeupItem, cartItemClickListener, deleteItemClickListener)
}
class DiffCallback : DiffUtil.ItemCallback<MakeupItem>() {
override fun areItemsTheSame(oldItem: MakeupItem, newItem: MakeupItem): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: MakeupItem, newItem: MakeupItem): Boolean {
return oldItem == newItem
}
}
class CartItemClickListener(val clickListener: (makeupItem: MakeupItem) -> Unit) {
fun onClick(makeupItem: MakeupItem) = clickListener(makeupItem)
}
class DeleteItemClickListener(val deleteItemClickListener: (makeupItem: MakeupItem) -> Unit) {
fun onClick(makeupItem: MakeupItem) = deleteItemClickListener(makeupItem)
}
}
Что касается CartListItem, добавьте два dataBinding, один itemClickListener, а другой для deleteButtonClickListener, и используйте android:onClick и лямбда-выражение внутри него.
<layout xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
xmlns:tools = "http://schemas.android.com/tools">
<data>
<variable
name = "makeupItem"
type = "com.melfouly.makeupshop.model.MakeupItem" />
<variable
name = "cartItemClickListener"
type = "com.melfouly.makeupshop.makeupcart.CartAdapter.CartItemClickListener" />
<variable
name = "deleteItemClickListener"
type = "com.melfouly.makeupshop.makeupcart.CartAdapter.DeleteItemClickListener" />
</data>
<com.google.android.material.card.MaterialCardView
android:id = "@+id/cart_card_item"
android:layout_width = "match_parent"
android:layout_height = "100dp"
android:layout_margin = "4dp"
android:backgroundTint = "@color/primary"
android:onClick = "@{() -> cartItemClickListener.onClick(makeupItem)}"
app:cardCornerRadius = "8dp"
app:cardElevation = "8dp">
<LinearLayout
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:weightSum = "6">
<ImageView
android:id = "@+id/item_image"
loadImage = "@{makeupItem}"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_gravity = "center"
android:layout_weight = "1"
android:scaleType = "fitCenter"
tools:src = "@tools:sample/avatars" />
<TextView
android:id = "@+id/item_name"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "2"
android:fontFamily = "@font/aclonica"
android:gravity = "center"
android:text = "@{makeupItem.name}"
android:textColor = "@color/black"
android:textStyle = "bold"
tools:text = "Item name" />
<TextView
android:id = "@+id/item_price"
loadPrice = "@{makeupItem}"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "2"
android:fontFamily = "@font/aclonica"
android:gravity = "center"
android:textColor = "@color/black"
tools:text = "5$" />
<Button
android:id = "@+id/delete_button"
android:layout_width = "0dp"
android:layout_height = "match_parent"
android:layout_weight = "1"
android:onClick = "@{() -> deleteItemClickListener.onClick(makeupItem)}"
app:icon = "@drawable/ic_baseline_delete_outline_24"
app:iconGravity = "end" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
Как только вы доберетесь до своего фрагмента и объявите свой адаптер, передайте параметры адаптера, которые будут выполнять определенную функцию viewModel или все, что вам нужно реализовать при каждом нажатии кнопки вашего cardItem и удаления
adapter = CartAdapter(CartAdapter.CartItemClickListener { makeupItem ->
viewModel.onMakeupItemClicked(makeupItem)
}, CartAdapter.DeleteItemClickListener { makeupItem ->
viewModel.deleteItemFromCart(makeupItem)
}
)
Я надеюсь, что это лучший практический ответ, который поможет всем, у кого есть такая же проблема.