Home > Enterprise >  java.lang.IndexOutOfBoundsException when removing value from Realtime Database
java.lang.IndexOutOfBoundsException when removing value from Realtime Database

Time:08-29

I have an app that showing the item that user have from Firebase Realtime Database to Android Studio RecycleView. For each item in the RecycleView, i've added a function for user to delete the item they want to remove.

Im using fridgeRef.child(getRef(position).getKey()).removeValue(); to delete the value. My JSON structure in Firebase is

 {
  "Ingredient": {
    "hgO9joLhmfh4Wjn7xYpyqcYmNOB3": {
      "Cashews": {
        "Expiry": "2023-01-21",
        "Ingredient": 1
      },
      "Macadamia Nuts": {
        "Expiry": "2022-11-22",
        "Ingredient": 1
      },
      "Pecans": {
        "Expiry": "2023-01-21",
        "Ingredient": 1
      },
      "Pine Nuts": {
        "Expiry": "2022-11-22",
        "Ingredient": 1
      },
      "Pistachios": {
        "Expiry": "2023-01-21",
        "Ingredient": 1
      }
    }
  }
}

The problem here, went i try to remove value the first value which is Cashew, it'll delete successfully, but went i try to delete Macadamia Nuts, it'll the data under it which is Pecans. The problem continue until Macadamia nuts left and went i try to delete it it'll show this error and the app will forced stop

java.lang.IndexOutOfBoundsException: Index: 4, Size: 3 at java.util.ArrayList.get(ArrayList.java:437) at com.firebase.ui.common.BaseObservableSnapshotArray.getSnapshot(BaseObservableSnapshotArray.java:70) at com.firebase.ui.database.FirebaseRecyclerAdapter.getRef(FirebaseRecyclerAdapter.java:114) at com.example.recipely.fridge$2.lambda$onBindViewHolder$0$com-example-recipely-fridge$2(fridge.java:163) at com.example.recipely.fridge$2$$ExternalSyntheticLambda0.onClick(Unknown Source:4)

fridge.java

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        ingredientView = inflater.inflate(R.layout.fragment_fridge, container, false);

        expiryNote = (LinearLayout) ingredientView.findViewById(R.id.expiryNote);
        HaveData = (ScrollView) ingredientView.findViewById(R.id.HaveData);
        noFridge = (LinearLayout) ingredientView.findViewById(R.id.noFridge);

        expiryNote.setVisibility(View.GONE);
        HaveData.setVisibility(View.GONE);
        myIngredientList = (RecyclerView) ingredientView.findViewById(R.id.ingredientList);
        myIngredientList.setLayoutManager(new LinearLayoutManager(getContext()));

        mAuth = FirebaseAuth.getInstance();
        currentUserID = mAuth.getCurrentUser().getUid();

        fridgeRef = FirebaseDatabase.getInstance().getReference().child("Ingredient").child(currentUserID);

        fridgeRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if(snapshot.exists()){
                    HaveData.setVisibility(View.VISIBLE);
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });

        return ingredientView;
    }

    @Override
    public void onStart() {
        super.onStart();


        FirebaseRecyclerOptions<fridgeItem> options =
                new FirebaseRecyclerOptions.Builder<fridgeItem>()
                        .setQuery(fridgeRef , fridgeItem.class)
                        .build();

        FirebaseRecyclerAdapter<fridgeItem, fridgeViewHolder> adapter
                = new FirebaseRecyclerAdapter<fridgeItem, fridgeViewHolder>(options) {
            @Override
            protected void onBindViewHolder(@NonNull fridgeViewHolder holder, int position, @NonNull fridgeItem model) {
                String itemName = getRef(position).getKey();

                LocalDate expiry = LocalDate.parse(model.getExpiry());
                LocalDate today = LocalDate.now();
                long dayDiff = DAYS.between(today, expiry);

                if(dayDiff <= 7){
                    holder.cardViewIngredient.setBackgroundColor(Color.parseColor("#FFD1D1"));
                    expiryNote.setVisibility(View.VISIBLE);
                }
                holder.ingredName.setText(itemName);
                holder.ingredExpiry.setText(dayDiff   " Days until expiry");

                holder.deleteBtn.setOnClickListener(v ->{
                    String output = fridgeRef.child(getRef(position).getKey()).toString();
                    Log.d("Fridge" , output );
                    fridgeRef.child(getRef(position).getKey()).removeValue();
                });
            }

            @NonNull
            @Override
            public fridgeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.ingredientrecycle, parent, false);
                fridgeViewHolder viewHolder = new fridgeViewHolder(view);
                return viewHolder;
            }
        };

        myIngredientList.setAdapter(adapter);
        adapter.startListening();
    }

    public static class fridgeViewHolder extends RecyclerView.ViewHolder{

        TextView ingredName, ingredExpiry;
        LinearLayout deleteBtn, cardViewIngredient;

        public fridgeViewHolder(@NonNull View itemView) {
            super(itemView);


            ingredName = itemView.findViewById(R.id.itemName);
            ingredExpiry = itemView.findViewById(R.id.itemExpiry);

            cardViewIngredient = itemView.findViewById(R.id.cardViewIngredient);
            //set button to delete an item
            deleteBtn = itemView.findViewById(R.id.deleteItem);
        }
    }
}

CodePudding user response:

The problem that you are using old positions with the delete function, for example in the beginning Pistachios is at position 4 but if you delete the other item it will became at position 0, that's why you get the error, the problem is on onBindViewHolder:

@Override
protected void onBindViewHolder(@NonNull fridgeViewHolder holder, int position, @NonNull fridgeItem model) {
    // the correct itemName is going to be stored here
    String itemName = getRef(position).getKey();

    LocalDate expiry = LocalDate.parse(model.getExpiry());
    LocalDate today = LocalDate.now();
    long dayDiff = DAYS.between(today, expiry);

    if(dayDiff <= 7){
        holder.cardViewIngredient.setBackgroundColor(Color.parseColor("#FFD1D1"));
        expiryNote.setVisibility(View.VISIBLE);
    }
    holder.ingredName.setText(itemName);
    holder.ingredExpiry.setText(dayDiff   " Days until expiry");

    holder.deleteBtn.setOnClickListener(v ->{
        // and here we will use the correct stored itemName instead of getting a new one
        String output = fridgeRef.child(itemName.toString();
        Log.d("Fridge" , output );
        fridgeRef.child(itemName).removeValue();
    });
}

This should fix your problem

  • Related