Android RecyclerView删除购物车Item崩溃问题排查求助
Android RecyclerView购物车删除Item崩溃问题(IndexOutOfBoundsException)
异常信息
FATAL EXCEPTION: main Process: com.example.hungryhopper, PID: 8373 java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 0
问题描述
在Android应用中使用RecyclerView展示Firebase存储的购物车数据,删除Item时数据已从购物车移除,但APP随即崩溃抛出上述索引越界异常。已确认列表初始化正常,日志验证position和列表均有有效值,商品数量修改功能正常。
相关代码
class CartAdapter( private val cartItems: MutableList<String>, private val cartItemsPrice: MutableList<String>, private val cartDescriptions: MutableList<String>, private val cartImages: MutableList<String>, private val cartQuantity: MutableList<Int>, private val cartIngredients: MutableList<String>, private val context: Context, private val onDeleteClickListener: (position: Int) -> Unit ) : RecyclerView.Adapter<CartAdapter.CartViewHolder>() { private val auth = FirebaseAuth.getInstance() init { val database = FirebaseDatabase.getInstance() val userId = auth.currentUser?.uid ?: "" val cartItemNumber = cartItems.size itemQuantities = IntArray(cartItemNumber) { 1 } cartItemsReference =database.reference.child(USER_NODE).child(userId).child(CARTITEM_NODE) } companion object { private var itemQuantities = intArrayOf() private lateinit var cartItemsReference: DatabaseReference } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder { return CartViewHolder( CartItemBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) } override fun getItemCount(): Int { return cartItems.size } override fun onBindViewHolder(holder: CartViewHolder, position: Int) { holder.bind(position) } inner class CartViewHolder(private val binding: CartItemBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(position: Int) { binding.apply { cartFoodName.text = cartItems[position] cartItemPrice.text = cartItemsPrice[position] quantity.text = itemQuantities[position].toString() val uriString = cartImages[position] val uri = Uri.parse(uriString.toString()) Glide.with(context).load(uri).into(cartImage) minusButton.setOnClickListener { decreaseQuantity(position) } plusButton.setOnClickListener { increaseQuantity(position) } deleteButton.setOnClickListener { deleteItem(position) } } } private fun decreaseQuantity(position: Int) { if (itemQuantities[position] > 1) { itemQuantities[position]-- cartQuantity[position] = itemQuantities[position] binding.quantity.text = itemQuantities[position].toString() } } private fun increaseQuantity(position: Int) { if (itemQuantities[position] < 10) { itemQuantities[position]++ cartQuantity[position] = itemQuantities[position] binding.quantity.text = itemQuantities[position].toString() } } private fun deleteItem(position: Int) { val positionRetrieve = position getUniqueAtPosition(positionRetrieve) { uniqueKey -> if (uniqueKey != null) { removeItem(position, uniqueKey) } } } } private fun removeItem(position: Int, uniqueKey: String) { cartItemsReference.child(uniqueKey).removeValue().addOnSuccessListener { cartItems.removeAt(position) cartImages.removeAt(position) cartDescriptions.removeAt(position) cartQuantity.removeAt(position) cartItemsPrice.removeAt(position) cartIngredients.removeAt(position) itemQuantities = itemQuantities.filterIndexed { index, i -> index != position }.toIntArray() notifyItemRemoved(position) notifyItemRangeChanged(position, cartItems.size) Toast.makeText(context, " Item Deleted", Toast.LENGTH_SHORT).show() }.addOnFailureListener { Toast.makeText(context, "Failed to Delete", Toast.LENGTH_SHORT).show() } } private fun getUniqueAtPosition(i: Int, onComplete: (String?) -> Unit) { cartItemsReference.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { var uniqueKey: String? = null snapshot.children.forEachIndexed { index, dataSnapshot -> if (index == i) { uniqueKey = dataSnapshot.key return@forEachIndexed } } onComplete(uniqueKey) } override fun onCancelled(error: DatabaseError) {} }) } fun getUpdatedItemQuantities(): MutableList<Int> { val itemQuantity = mutableListOf<Int>() itemQuantity.addAll(cartQuantity) return itemQuantity } }
问题根源与修复方案
1. 静态变量引发的共享冲突
companion object中的itemQuantities是全局静态变量,多个Adapter实例会共享该数组,删除操作后数组长度与本地列表不一致,导致索引越界。
修复:将itemQuantities和cartItemsReference改为Adapter实例变量,移除companion object中的定义:
class CartAdapter( // 原有参数 ) : RecyclerView.Adapter<CartAdapter.CartViewHolder>() { private val auth = FirebaseAuth.getInstance() private var itemQuantities = intArrayOf() private lateinit var cartItemsReference: DatabaseReference init { val database = FirebaseDatabase.getInstance() val userId = auth.currentUser?.uid ?: "" val cartItemNumber = cartItems.size itemQuantities = IntArray(cartItemNumber) { 1 } cartItemsReference = database.reference.child(USER_NODE).child(userId).child(CARTITEM_NODE) } // 移除原companion object }
2. 异步回调中使用过时position
getUniqueAtPosition是异步操作,若用户连续删除Item,回调触发时原position对应的列表项可能已被移除,导致索引越界。
修复:在回调前后检查position有效性:
private fun deleteItem(position: Int) { if (position < 0 || position >= cartItems.size) return val currentPosition = position getUniqueAtPosition(currentPosition) { uniqueKey -> uniqueKey?.let { if (currentPosition < cartItems.size) { removeItem(currentPosition, it) } } } }
3. Firebase节点匹配方式不可靠
通过forEachIndexed按position匹配Firebase节点key的方式不严谨,Firebase节点顺序可能与本地列表不一致,导致删除错误节点,进而引发数据不同步。
修复:加载购物车数据时,将Firebase节点key与商品数据绑定(建议创建CartItem数据类),删除时直接使用存储的key:
// 定义数据类 data class CartItem( val id: String, // Firebase节点key val name: String, val price: String, val description: String, val imageUrl: String, var quantity: Int, val ingredients: String ) // 修改Adapter参数为单个CartItem列表 class CartAdapter( private val cartItems: MutableList<CartItem>, private val context: Context, private val onDeleteClickListener: (position: Int) -> Unit ) : RecyclerView.Adapter<CartAdapter.CartViewHolder>() { // 内部逻辑对应修改,比如bind时直接调用cartItems[position].name private fun deleteItem(position: Int) { if (position < 0 || position >= cartItems.size) return val targetItem = cartItems[position] cartItemsReference.child(targetItem.id).removeValue().addOnSuccessListener { cartItems.removeAt(position) itemQuantities = itemQuantities.filterIndexed { index, _ -> index != position }.toIntArray() notifyItemRemoved(position) notifyItemRangeChanged(position, cartItems.size) Toast.makeText(context, "Item Deleted", Toast.LENGTH_SHORT).show() } } }
内容的提问来源于stack exchange,提问作者Yogesh Aggarwal
相关产品推荐
相关产品推荐

