You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 21:15:35