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
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
}
}
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: