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

Android Kotlin中使用RoomDB更新购物车并计算总价问题排查

问题排查与解决方案

问题根源

  1. 内存集合与Room不同步:更新Room数据库后,未同步更新Adapter持有的listItems集合,导致计算总价时使用旧数据。
  2. 总价计算依赖初始化集合:Fragment的onUpdateTotal回调直接使用初始化时的listitem集合,该集合未随Room更新。
  3. Adapter资源初始化冗余:onBindViewHolder中重复初始化database和dao,造成不必要的资源消耗。
  4. 局部变量导致状态丢失:增减按钮的itemcart为局部变量,切换toggle按钮会重置库存,且未从现有Cart对象读取当前库存值。

修复步骤

1. 优化CartAdapter,同步内存与Room数据

class CartAdapter(
    private val context: Context,
    private val listItems: ArrayList<Cart>,
    private val listener: OnAdapterListener
) : RecyclerView.Adapter<CartAdapter.CartViewHolder>() {

    private val dao: CartDao

    interface OnAdapterListener{
        fun onUpdateTotal()
    }

    // 初始化数据库连接到构造函数
    init {
        val database = CartRoomDatabase.getDatabase(context)
        dao = database.getCartDao()
    }

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

    override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
        val item = listItems[position]
        holder.tv_namaproduk.text = item.namaproduk
        holder.tv_stokproduk.text = item.stokproduk.toString()
        holder.tv_hargaproduk.text = (item.stokproduk * item.hargaproduk).toString()

        Glide.with(holder.iv_gambarproduk)
            .load(item.gambarproduk)
            .error(R.drawable.bannerwelcome)
            .into(holder.iv_gambarproduk)

        // 从当前item读取库存作为初始值
        var itemcart = item.stokproduk

        holder.itemView.togglebtneg.addOnButtonCheckedListener { _, checkedId, isChecked ->
            if (isChecked){
                when(checkedId){
                    R.id.btn_ecer ->{
                        holder.itemView.iv_tambahstok.setOnClickListener {
                            itemcart++
                            updateItemAndDatabase(position, itemcart)
                            updateItemUI(holder, itemcart, item.hargaproduk)
                            listener.onUpdateTotal()
                        }
                        holder.itemView.iv_kurangstok.setOnClickListener {
                            if (itemcart > 0) {
                                itemcart--
                                updateItemAndDatabase(position, itemcart)
                                updateItemUI(holder, itemcart, item.hargaproduk)
                                listener.onUpdateTotal()
                            }
                        }
                    }
                    R.id.btn_grosir ->{
                        holder.itemView.iv_tambahstok.setOnClickListener {
                            itemcart +=5
                            updateItemAndDatabase(position, itemcart)
                            updateItemUI(holder, itemcart, item.hargaproduk)
                            listener.onUpdateTotal()
                        }
                        holder.itemView.iv_kurangstok.setOnClickListener {
                            if (itemcart >=5) {
                                itemcart -=5
                                updateItemAndDatabase(position, itemcart)
                                updateItemUI(holder, itemcart, item.hargaproduk)
                                listener.onUpdateTotal()
                            }
                        }
                    }
                }
            }
        }

        holder.itemView.iv_deletecart.setOnClickListener {
            dao.delete(item)
            listItems.removeAt(position)
            notifyItemRemoved(position)
            notifyItemRangeChanged(position, listItems.size)
            listener.onUpdateTotal()
        }
    }

    override fun getItemCount(): Int = listItems.size

    // 同步更新内存集合与数据库
    private fun updateItemAndDatabase(position: Int, newStock: Int) {
        val updatedCart = listItems[position].copy(stokproduk = newStock)
        dao.update(updatedCart)
        listItems[position] = updatedCart
        notifyItemChanged(position)
    }

    // 更新单个item的UI
    private fun updateItemUI(holder: CartViewHolder, stock: Int, price: Int) {
        holder.tv_stokproduk.text = stock.toString()
        holder.tv_hargaproduk.text = (stock * price).toString()
    }

    class CartViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tv_namaproduk = itemView.findViewById<TextView>(R.id.tv_namaprodukfin)
        val tv_hargaproduk = itemView.findViewById<TextView>(R.id.tv_hargaprodukfin)
        val tv_stokproduk = itemView.findViewById<TextView>(R.id.tv_banyakstok)
        val iv_gambarproduk = itemView.findViewById<ImageView>(R.id.iv_cartitem)
    }
}

2. 修复KeranjangFragment的总价计算逻辑

确保总价计算使用最新数据,可选两种方案:

private fun getCartList() {
    val sharedpreferencesLogged = requireActivity().getSharedPreferences("sp_login", android.content.Context.MODE_PRIVATE)
    val loginid = sharedpreferencesLogged.getString("login_id", null)
    val database = CartRoomDatabase.getDatabase(requireContext().applicationContext)
    val dao = database.getCartDao()
    val listitem = arrayListOf<Cart>()
    listitem.addAll(dao.getAll())

    if(loginid == null){
        scrollview_profile_keranjang.visibility = View.VISIBLE
        ln_filledcart.visibility = View.INVISIBLE
        ln_emptycart.visibility = View.INVISIBLE
    }else{
        scrollview_profile_keranjang.visibility = View.INVISIBLE
        if (listitem.size == 0){
            ln_filledcart.visibility = View.INVISIBLE
            ln_emptycart.visibility = View.VISIBLE
        } else {
            ln_filledcart.visibility = View.VISIBLE
            rv_cartitem.layoutManager = LinearLayoutManager(activity)
            val adaptertemp = CartAdapter(requireContext(), listitem, object : CartAdapter.OnAdapterListener{
                override fun onUpdateTotal() {
                    // 方案1:使用已同步的内存集合计算
                    var sum = 0
                    listitem.forEach { cart ->
                        sum += cart.hargaproduk * cart.stokproduk
                    }
                    tv_total.text = sum.toString()

                    // 方案2:从Room重新获取最新数据(更可靠)
                    // val latestItems = dao.getAll()
                    // var sum = 0
                    // latestItems.forEach { cart ->
                    //     sum += cart.hargaproduk * cart.stokproduk
                    // }
                    // tv_total.text = sum.toString()
                }
            })
            rv_cartitem.adapter = adaptertemp
            ln_emptycart.visibility = View.INVISIBLE
        }
    }
}

3. 额外优化建议

  • 使用Room的LiveData监听数据变化:将dao.getAll()改为返回LiveData<List<Cart>>,Fragment中观察LiveData,数据更新时自动刷新Adapter和总价。
  • 避免在onBindViewHolder中重复设置点击监听器,改为在onCreateViewHolder中绑定,通过holder.adapterPosition获取位置。

内容的提问来源于stack exchange,提问作者Random Thought

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 15:45:43