Search code examples
androidfirebasefirebase-realtime-databaseandroid-recyclerviewfirebaseui

getting java.lang.IndexOutOfBoundsException: Index: 3, Size: 3 while deleting items from firebaseui recyclerview


I have a list of items that I am retrieving from Firebase. To delete those items I have implemented a delete button. I am trying to get the post key after the user clicks the delete button. Currently I am able to delete items, but when I delete something from the middle of the RecyclerView and then and then the first item, the app crashes.

Code:

public class MyPosts extends AppCompatActivity {

    private static final String TAG = "MyPosts";

    Toolbar toolbar;
    DatabaseReference WorkingData;
    DatabaseReference WorkingDataForDeletion;
    DatabaseReference tasks;

    private BottomNavigationView navigationView;
    RecyclerView recyclerView;
    ArrayList<WorkInformation> workList;

    private AppCompatButton deletePost;
    private AppCompatButton editPost;
    String currentUserID, postKey;
    private FirebaseAuth firebaseAuth;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_posts);

        toolbar = (Toolbar) findViewById(R.id.my_post_toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle("My Posts");
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        navigationView = findViewById(R.id.bottomNavigationView);
        firebaseAuth = FirebaseAuth.getInstance();

        currentUserID = firebaseAuth.getCurrentUser().getUid();
        WorkingData = FirebaseDatabase.getInstance().getReference().child("UserIndividualTasks");
        WorkingData.keepSynced(true);

        recyclerView = findViewById(R.id.my_post_recycler_view);
        recyclerView.setHasFixedSize(true);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setReverseLayout(true);
        linearLayoutManager.setStackFromEnd(true);
        recyclerView.setLayoutManager(linearLayoutManager);

        deletePost = findViewById(R.id.myPostDelete);
        editPost = findViewById(R.id.myPostEdit);

    }


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


    }


    @Override
    protected void onResume() {
        super.onResume();

        Query query = WorkingData.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
        FirebaseRecyclerOptions<WorkInformation> options = new FirebaseRecyclerOptions.Builder<WorkInformation>()
                .setQuery(query, WorkInformation.class)
                .build();


        final FirebaseRecyclerAdapter<WorkInformation, MyViewHolder> adapter = new FirebaseRecyclerAdapter<WorkInformation, MyViewHolder>(options) {
            @Override
            protected void onBindViewHolder(@NonNull MyViewHolder holder, final int position, @NonNull WorkInformation model) {


                holder.title.setText(model.getWorkTitle());
                holder.date.setText(model.getWorkDueDate());
                Picasso.get().load(model.getImg()).into(holder.imageView);


                holder.deleteBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        notifyDataSetChanged();
                        postKey = getRef(position).getKey();


                        Log.d(TAG, "onClick: position is " + position);

                        Log.d(TAG, "onClick: post key is " + postKey);

                        notifyItemRemoved(position);
                        WorkingDataForDeletion = FirebaseDatabase.getInstance().getReference().child("UserIndividualTasks")
                                .child(currentUserID)
                                .child(postKey);
                        WorkingDataForDeletion.removeValue();



                        notifyItemRemoved(position);
                        notifyDataSetChanged();


                    }
                });
            }

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

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

        adapter.notifyDataSetChanged();

        recyclerView.setAdapter(adapter);
        adapter.startListening();
        adapter.notifyDataSetChanged();


    }


    public static class MyViewHolder extends RecyclerView.ViewHolder {

        public AppCompatTextView title;
        public AppCompatTextView date;
        public CircleImageView imageView;
        public AppCompatButton deleteBtn;
        public AppCompatButton editBtn;
        public ConstraintLayout constraintLayout;

        public MyViewHolder(View itemView) {
            super(itemView);

            title = itemView.findViewById(R.id.my_posts_title);
            date = itemView.findViewById(R.id.myPostDate);
            imageView = itemView.findViewById(R.id.UserImage);
            deleteBtn = itemView.findViewById(R.id.myPostDelete);
            editBtn = itemView.findViewById(R.id.myPostEdit);
        }
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case android.R.id.home:
                startActivity(new Intent(MyPosts.this, Services.class));
                break;
        }


        return super.onOptionsItemSelected(item);
    }

    private void deleteUserPost() {

    }

}

Crash log:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.example.saqib.smarttaskk, PID: 19583
              java.lang.IndexOutOfBoundsException: Index: 3, 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:112)
                  at com.example.saqib.smarttaskk.MyPosts$1$1.onClick(MyPosts.java:131)
                  at android.view.View.performClick(View.java:6891)
                  at android.widget.TextView.performClick(TextView.java:12651)
                  at android.view.View$PerformClick.run(View.java:26083)
                  at android.os.Handler.handleCallback(Handler.java:789)
                  at android.os.Handler.dispatchMessage(Handler.java:98)
                  at android.os.Looper.loop(Looper.java:164)
                  at android.app.ActivityThread.main(ActivityThread.java:6938)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

Solution

  • Don't ever use position inside of listeners or asynchronous tasks.

    Inside your OnClickListener, replace any use of position with holder.getAdapterPosition().

    You should also remove all calls to any sort of data notification (notifyDataSetChanged(), notifyItemRemoved(), etc), inside or outside your adapter.

    Your final onClick() method should look like this:

    public void onClick(View v) {
        //removed notifyDataSetChanged()
        postKey = getRef(holder.getAdapterPosition()).getKey();
    
        Log.d(TAG, "onClick: position is " + holder.getAdapterPosition());
    
        Log.d(TAG, "onClick: post key is " + postKey);
    
        //removed notifyItemRemoved()
    
        //variable names should be camelCase not TitleCase
        workingDataForDeletion = FirebaseDatabase.getInstance().getReference().child("UserIndividualTasks")
                .child(currentUserID)
                .child(postKey);
        workingDataForDeletion.removeValue();
    
        //removed notifyItemRemoved()
        //removed notifyDataSetChanged()
    }