RecyclerView Item中DataBinding与Fragment ViewModel绑定失效问题
问题描述
我有一个展示购物车商品列表的RecyclerView,每个列表项点击后可打开对应商品的详情Fragment。目前正在更新列表项布局,添加删除按钮,该按钮需调用Fragment的ViewModel中的删除方法。我认为将ViewModel作为Adapter的构造参数不符合关注点分离原则,尝试通过DataBinding实现但未解决问题。
解决方案
通过给Adapter新增删除回调的方式实现需求,既遵守关注点分离原则,又无需在DataBinding中直接绑定ViewModel。核心思路是让Adapter只负责触发点击事件,具体的逻辑(打开详情、调用ViewModel删除)由Fragment处理。
修改后的代码实现
1. 更新CartAdapter
添加删除回调参数,并在ViewHolder中绑定按钮点击事件:
class CartAdapter( private val itemClickListener: (MakeupItem) -> Unit, private val deleteClickListener: (MakeupItem) -> Unit ) : ListAdapter<MakeupItem, CartAdapter.CartViewHolder>(DiffCallback()) { class CartViewHolder(private val binding: CartListItemBinding) : RecyclerView.ViewHolder(binding.root) { fun bind( makeupItem: MakeupItem, onItemClick: (MakeupItem) -> Unit, onDeleteClick: (MakeupItem) -> Unit ) { binding.makeupItem = makeupItem // 绑定删除按钮点击事件 binding.deleteButton.setOnClickListener { onDeleteClick(makeupItem) } // 绑定列表项点击事件 binding.root.setOnClickListener { onItemClick(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.bind(makeupItem, itemClickListener, deleteClickListener) } 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 } } }
2. 简化CartListItem.xml
移除布局中绑定的ViewModel变量,无需直接在XML中调用ViewModel方法:
<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" /> </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" app:icon="@drawable/ic_baseline_delete_outline_24" app:iconGravity="end" /> </LinearLayout> </com.google.android.material.card.MaterialCardView> </layout>
3. 在CartFragment中初始化Adapter
实现两个回调,分别处理列表项跳转和删除逻辑:
class CartFragment : Fragment() { private val viewModel: CartViewModel by viewModels() private lateinit var adapter: CartAdapter private lateinit var binding: CartFragmentBinding // 假设你用了Fragment的DataBinding override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = CartFragmentBinding.bind(view) // 初始化Adapter adapter = CartAdapter( itemClickListener = { makeupItem -> // 打开商品详情Fragment的逻辑 navigateToProductDetail(makeupItem) }, deleteClickListener = { makeupItem -> // 调用ViewModel的删除方法 viewModel.deleteItemFromCart(makeupItem) } ) binding.recyclerView.adapter = adapter // 观察购物车数据变化,更新列表 viewModel.cartItems.observe(viewLifecycleOwner) { cartItems -> adapter.submitList(cartItems) } } private fun navigateToProductDetail(item: MakeupItem) { // 此处实现跳转商品详情Fragment的逻辑 } }
4. 保留原CartViewModel的删除方法
fun deleteItemFromCart(makeupItem: MakeupItem) { viewModelScope.launch { Log.d(TAG, "DeleteItemFromCart method in viewModel called") repository.deleteItemFromCart(makeupItem) } }
内容的提问来源于stack exchange,提问作者Mahmoud Reda
相关产品推荐
相关产品推荐

