GGG
GGG

Reputation: 141

How to fix duplication of item when deleted from RecyclerView?

I am currently developing an app and I am using RecyclerView adapter, in the adapter I use listeners, to get buttons to work in every recyclers item. Now I have an option to delete the recycler item and everything is fine if the item is the last item in the RecyclerView. However, if it is at the beginning or the middle it gets replaced with the item which is next to it and the item duplicates.

Now this is the code for the activity

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note);
mDbAdapter = new NotesDbAdapter(this);
mDbAdapter.open();
listNotes = mDbAdapter.getResults();
mNotesAdapter = new NotesAdapter(this, mDbAdapter.fetchAllNotes(),listNotes);
mNotesAdapter.notifyDataSetChanged();
mRecyclerView = findViewById(R.id.listView_notes);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mNotesAdapter);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

This is the adapter code:

The constructor:

public NotesAdapter(Context ctx, Cursor cursor, ArrayList<Note> listNotes){
    this.mCtx = ctx;
    this.mCursor = cursor;
    this.mListNotes = listNotes;
}

And the onBindViewHolder

//BIND DATA TO VIEWS
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    if (!mCursor.moveToPosition(position)) {
        return;
    }

    String noteT = mCursor.getString(mCursor.getColumnIndex(NotesDbAdapter.COL_TITLE));
    String noteC = mCursor.getString(mCursor.getColumnIndex(NotesDbAdapter.COL_CONTENT));
    holder.noteTitle.setText(noteT);
    holder.noteDescription.setText(noteC);
    holder.noteCard_settingsButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mDbAdapter = new NotesDbAdapter(mCtx);
            mDbAdapter.open();
            Toast.makeText(mCtx,"The position is:"+holder.getAdapterPosition(),Toast.LENGTH_SHORT).show();
            PopupMenu popup = new PopupMenu(mCtx, view);
            MenuInflater inflater = popup.getMenuInflater();
            inflater.inflate(R.menu.menu_popup, popup.getMenu());
            popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.popup_edit:
                            Toast.makeText(mCtx,"You pressed edit note", Toast.LENGTH_SHORT).show();
                            return true;
                        case R.id.popup_delete:
                            deleteItem(holder.getAdapterPosition());
                            Toast.makeText(mCtx, "Delete note", Toast.LENGTH_SHORT).show();
                            return true;
                    }
                    return false;
                }
            });
            popup.show();
        }
    });
}

And the other functions are like the following.

@Override
public int getItemCount() {
    return mListNotes.size();
}

@Override
public long getItemId(int position) {
    return position;
}

private void deleteItem(int position) {
    mDbAdapter.deleteNoteById(mListNotes.get(position).getId());
    mListNotes.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeRemoved(position, 1);
}

public class ViewHolder extends RecyclerView.ViewHolder{
    TextView noteTitle;
    TextView noteDescription;
    CardView cardView;
    Button noteCard_settingsButton;

    public ViewHolder(View view) {
        super(view);
        noteTitle = view.findViewById(R.id.note_titleView);
        noteDescription = view.findViewById(R.id.note_contentView);
        cardView = view.findViewById(R.id.notes_cardView);
        noteCard_settingsButton = view.findViewById(R.id.note_card_settings_button);
    }
}

So I expect the note to be fully removed and the notes after it, moved down.

Upvotes: 1

Views: 1325

Answers (3)

Jignesh Mayani
Jignesh Mayani

Reputation: 7203

Try below code:

private void deleteItem(int position) {
    mDbAdapter.deleteNoteById(mListNotes.get(position).getId());
    mListNotes.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(0, mListNotes.size()); //change here
}

Upvotes: 1

Reaz Murshed
Reaz Murshed

Reputation: 24231

Looks like you are not updating the mCursor that you are using in your onBindViewHolder function. Though you are updating the mListNotes, you are still populating the data in each item of your RecyclerView from the cursor and the cursor is not being updated when you are deleting an item from the list.

I would rather recommend getting the data from your ArrayList (i.e. mListNotes) instead of the cursor in your onBindViewHolder. Modify the constructor like the following.

// Remove the mCursor completely.
// Looks like you do not need that. 
public NotesAdapter(Context ctx, ArrayList<Note> listNotes){
    this.mCtx = ctx;
    this.mListNotes = listNotes;
} 

And in your onBindViewHolder take the data from the mListNotes.

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    String noteT = mListNotes.get(position).getTitle();
    String noteC = mListNotes.get(position).getContent();

    holder.noteTitle.setText(noteT);
    holder.noteDescription.setText(noteC);
    holder.noteCard_settingsButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mDbAdapter = new NotesDbAdapter(mCtx);
            mDbAdapter.open();
            Toast.makeText(mCtx,"The position is:"+holder.getAdapterPosition(),Toast.LENGTH_SHORT).show();
            PopupMenu popup = new PopupMenu(mCtx, view);
            MenuInflater inflater = popup.getMenuInflater();
            inflater.inflate(R.menu.menu_popup, popup.getMenu());
            popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.popup_edit:
                            Toast.makeText(mCtx,"You pressed edit note", Toast.LENGTH_SHORT).show();
                            return true;
                        case R.id.popup_delete:
                            deleteItem(holder.getAdapterPosition());
                            Toast.makeText(mCtx, "Delete note", Toast.LENGTH_SHORT).show();
                            return true;
                    }
                    return false;
                }
            });
            popup.show();
        }
    });
}

I hope that fixes your problem.

In case you want to keep the cursor in your adapter, then make sure the cursor passed to the adapter is updated as well.

Upvotes: 1

csar
csar

Reputation: 131

It might be caused by

notifyItemRemoved(position);
notifyItemRangeRemoved(position, 1);

You only need the call notifyItemRemoved(position);

Upvotes: 0

Related Questions