androidkotlinandroid-recyclerviewandroid-arrayadaptershopping-cart

Update the cart total amount after delete item from shopping cart


This is an e-commerce app, I am facing an issue related to (adding/removing) items from the cart, The problem happened when I added two items and then if I removed one of them, The total amount is not updated correctly unless I closed the MyCartFragment and re-open it again,

This MyCartFragment looks like when it has a one-item

enter image description here

Firstly this CartItemModel class and it have two companion objects one is CART_ITEM for the regular cart item, and the second TOTAL_AMOUNT for calculating the price and total amount

@Parcelize
data class CartItemModel(
    var type: Int?,
    var productId: String?,
    var productImage: String?,
    var productName: String?,
    var freeCoupons: Long?,
    var productPrice: String?,
    var cuttedPrice: String?,
    var productQuantity: Long?,
    var maxQuantity: Long?,
    var stockQuantity: Long?,
    var offersApply: Long?,
    var couponsApplied: Long?,
    var inStock: Boolean?,
    var qtyIDs: ArrayList<String>?,
    var selectedCouponId: String?,
    var discountedPrice: String?,

    var totalItems: Int?,
    var totalItemsPrice: Int?,
    var deliveryPrice: String?,
    var totalAmount: Int?,
    var savedAmount: Int?
) : Parcelable {

    //
    constructor(type: Int) : this(
        type,
        productId = null,
        productImage = null,
        productName = null,
        freeCoupons = null,
        productPrice = null,
        cuttedPrice = null,
        productQuantity = null,
        maxQuantity = null,
        stockQuantity = null,
        offersApply = null,
        couponsApplied = null,
        inStock = null,
        qtyIDs = null,
        selectedCouponId = null,
        discountedPrice = null,
        totalItems = null,
        totalItemsPrice = null,
        deliveryPrice = null,
        totalAmount = null,
        savedAmount = null
    ) {
        this.type = type
    }

//    var isInStock = false


    ////////CART TOTAL

    constructor(
        type: Int,
        totalItems: Int,
        totalItemsPrice: Int,
        deliveryPrice: String,
        totalAmount: Int,
        savedAmount: Int
    ) : this(
        type, null, null, null,
        null, null, null,
        null, null, null,
        null, null, null,
        null, null, null,
        totalItems, totalItemsPrice, deliveryPrice, totalAmount, savedAmount
    )


    companion object {
        const val CART_ITEM = 0
        const val TOTAL_AMOUNT = 1
    }
}

This part from the CartAdapter class specifically onCreateViewHolder and onBindViewHolder, in the second part of onBinViewHolder when the item is CartItemModel.TOTAL_AMOUNT here where I calculate the total items price and the discounts, etc

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            CartItemModel.CART_ITEM -> {
                val cartItemLayoutBinding: CartItemLayoutBinding = CartItemLayoutBinding.inflate(
                    LayoutInflater.from(parent.context), parent, false
                )
                CartItemViewHolder(cartItemLayoutBinding)
            }

            CartItemModel.TOTAL_AMOUNT -> {
                val cartTotalAmountLayoutBinding: CartTotalAmountLayoutBinding =
                    CartTotalAmountLayoutBinding.inflate(
                        LayoutInflater.from(parent.context), parent, false
                    )
                CartTotalAmountViewHolder(cartTotalAmountLayoutBinding)
            }

            else -> {
                val cartItemLayoutBinding: CartItemLayoutBinding = CartItemLayoutBinding.inflate(
                    LayoutInflater.from(parent.context), parent, false
                )
                CartItemViewHolder(cartItemLayoutBinding)
            }
        }
    }

    override fun onBindViewHolder(
        holder: RecyclerView.ViewHolder,
        @SuppressLint("RecyclerView") position: Int
    ) {
        when (asyncListDiffer.currentList[position].type) {
            CartItemModel.CART_ITEM -> {
                val cartItemModel: CartItemModel = asyncListDiffer.currentList[position]

                (holder as CartItemViewHolder).bind(cartItemModel, position)

                if (lastPosition < position) {
                    val animation = AnimationUtils.loadAnimation(
                        holder.itemView.context,
                        R.anim.fade_in
                    )
                    holder.itemView.animation = animation

                    lastPosition = position
                }
            }

            CartItemModel.TOTAL_AMOUNT -> {

                var totalItems = 0
                var totalItemsPrice = 0
                val deliveryPrice: String
                val totalAmount: Int
                var savedAmount = 0
//                var i = 0
                for (i in 0 until asyncListDiffer.currentList.size) {
                    if (asyncListDiffer.currentList[i].type == CartItemModel.CART_ITEM
                        && asyncListDiffer.currentList[i].inStock == true) {

                        val quantity = asyncListDiffer.currentList[i].productQuantity

                        totalItems = (totalItemsPrice + quantity!!).toInt()

                        totalItemsPrice += if (asyncListDiffer.currentList[position].selectedCouponId.isNullOrEmpty()) {
                            asyncListDiffer.currentList[i].productPrice?.toInt()!! * quantity.toInt()
                        } else {
                            asyncListDiffer.currentList[i].discountedPrice?.toInt()!! * quantity.toInt()
                        }

                        if (asyncListDiffer.currentList[i].cuttedPrice?.isNotEmpty()!!) {
                            savedAmount += (asyncListDiffer.currentList[i].cuttedPrice?.toInt()!! - asyncListDiffer.currentList[i].productPrice?.toInt()!!) * quantity.toInt()

                            if (!asyncListDiffer.currentList[position].selectedCouponId.isNullOrEmpty()) {
                                savedAmount += (asyncListDiffer.currentList[i].productPrice?.toInt()!! - asyncListDiffer.currentList[i].discountedPrice?.toInt()!!) * quantity.toInt()
                            }

                        } else {
                            if (asyncListDiffer.currentList[position].selectedCouponId?.isNotEmpty()!!) {
                                savedAmount += (asyncListDiffer.currentList[i].productPrice?.toInt()!! - asyncListDiffer.currentList[i].discountedPrice?.toInt()!!) * quantity.toInt()
                            }
                        }

                    }
                }
                if (totalItemsPrice > 500) {
                    deliveryPrice = "Free"
                    totalAmount = totalItemsPrice
                } else {
                    deliveryPrice = "60"
                    totalAmount = totalItemsPrice + 60
                }


                asyncListDiffer.currentList[position].totalItems = totalItems
                asyncListDiffer.currentList[position].totalItemsPrice = totalItemsPrice
                asyncListDiffer.currentList[position].deliveryPrice = deliveryPrice
                asyncListDiffer.currentList[position].totalAmount = totalAmount
                asyncListDiffer.currentList[position].savedAmount = savedAmount


                Log.d(TAG, "onBindViewHolder: totalItems $totalItems")
                Log.d(TAG, "onBindViewHolder: totalItems $totalItemsPrice")
                Log.d(TAG, "onBindViewHolder: totalItems $deliveryPrice")
                Log.d(TAG, "onBindViewHolder: totalItems $totalAmount")
                Log.d(TAG, "onBindViewHolder: totalItems $savedAmount")



                (holder as CartTotalAmountViewHolder).setTotalAmount(
                    totalItems, totalItemsPrice, deliveryPrice, totalAmount, savedAmount
                )
                myCartUtil?.getTotalAmount(totalAmount)

                if (lastPosition < position) {
                    val animation = AnimationUtils.loadAnimation(
                        holder.itemView.context,
                        R.anim.fade_in
                    )
                    holder.itemView.animation = animation

                    lastPosition = position
                }


            }

            else -> return
        }
    }

Solution

  • I guess that is because you are changing data in-place in your list of items. I suppose that is the problem:

    asyncListDiffer.currentList[position].totalItems = totalItems
    

    In this case, your adapter will never know what data is changed and what should be notified.

    val changedList = asyncListDiffer.currentList.toMutableList()
    changedList[position] = asyncListDiffer.currentList[position].copy( totalItems = totalItems, 
    totalItemsPrice = totalItemsPrice, 
    deliveryPrice = deliveryPrice,
    totalAmount = totalAmount,
    savedAmount = savedAmount)
     submitList(changedList) // infinite loop here?
     
    

    I described the conceptual change here. There are multiple changes that are required to make your app more stable and robust: