2 votes

RecyclerView: When deleting, the item below is duplicated.

I've been having a problem for a long time, I've looked everywhere and they always give the same solution but it doesn't work for me.

I have a recycler view that when I make a horizontal swipe I delete the item from the data. the item is deleted correctly, the data is correct, but the recycler view replicates the data wrong...

What it does is to duplicate the data just below it, I can't find a solution to this, I don't know if the library has a bug (native library).

I leave here some data

DEPENDENCIES:

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:animated-vector-drawable:28.0.0'
implementation 'com.android.support:customtabs:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'

RV Adapter:

 class AdapterRecycler extends RecyclerView.Adapter<AdapterRecycler.ViewHolderContactos> {
    ArrayList<c_Contactos> mListaContactos;

    AdapterRecycler(ArrayList<c_Contactos> mListaContactos) {
        this.mListaContactos = mListaContactos;
    }

    void eliminarItem(int index) {
        mListaContactos.remove(index);
        // adapterContactos = new AdapterRecycler(mListaContactos);
        // recyclerView.setAdapter(adapterContactos);
        notifyItemRemoved(index);
        // notifyDataSetChanged();
           /* adapterContactos.notifyItemRemoved(position);
            adapterContactos.notifyItemRangeChanged(position,adapterContactos.mListaContactos.size());
           adapterContactos.mListaContactos.remove(position);
            recyclerView.removeViewAt(position);
            adapterContactos.notifyDataSetChanged();
            adapterContactos.notifyItemRemoved(position);*/
    }

    @NonNull
    @Override
    public AdapterRecycler.ViewHolderContactos onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        return new ViewHolderContactos(new cc_Contactos(getApplicationContext(), mListaContactos.get(i), (i % 2 == 0)));
    }

    @Override
    public void onBindViewHolder(@NonNull AdapterRecycler.ViewHolderContactos holder, int i) {
    }

    @Override
    public long getItemId(int position) {
        return mListaContactos.get(position).hashCode();
        // return position;
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

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

    class ViewHolderContactos extends RecyclerView.ViewHolder {
        ViewHolderContactos(@NonNull View itemView) {
            super(itemView);
        }

    }
}

Check the ELIMINATE method, there I left commented all the things I tried and none worked, and now I upload a video of what happens.

introducir la descripción de la imagen aquí

0 votes

Try temporarily disabling or removing the override from getItemId of your Adapter . The itemId depends on the values of your list in, specifically that position . If your list changes in that position, i.e., the item that was there in that position, no longer exists in that specific position and then you notify directly to the Adapter to reflect a change, the adapter will call the method getItemId(int position) of each element and will update only those elements whose id has changed. In this case, it will change the items that follow from that position, because you are removing.

0 votes

Are you using local database Cursors, such as SQLite, and is that all the code, because if you have code in the onBindViewHolder, that may be the problem. Since you may not be setting a case of Rupture. Then it would rule out the getItemId . If you have a code in the onBindViewHolder Please share it by editing your question to show you the solution.

0 votes

@Andrespengineer How can I remove the override if necessary, I will try to figure out what you are telling me and confirm what I can do. I am not using cursors from a DB, I just add contacts to a list and use them from there. The RV adapter is complete, no method is missing or touched.

4voto

Andrespengineer Points 2553

The problem is that the views are being left in the state of DirtyView .

When a new item is displayed or a change is reported to the adapter, a view of the recycle group is taken for reuse. view is taken from the recycling group for reuse. Because the adapter must rejoin this view before it is this view before it is displayed, it is called Dirty View. View).

The dirty view ( DirtyView ) is recycled: the adapter locates the data for the next element to be displayed and copies this data to the next element to be displayed. for the next item to be displayed and copies this data into the views for this item. the views of this item. The references of these views are obtained from the The references of these views are obtained from the view holder associated with the recycled view.

You can see more details at This Answer where I explain a little more about RecyclerView and the problem of duplicating data.

Solution:


1. Instantiate the views of your layout in the class ViewHolderContactos .

2. In the onBindViewHolder you need to modify all the views to assign their properties. But why, because every time an element changes, the bind takes care of making sure that its properties are the correct ones and the DirtyView or dirty view, can be reset to its proper value in the list.

An example of how to instantiate the views and assign their corresponding value:

. . .

// Esta es la clase ViewHolder, el layout item inflado por viewType
public static class ViewHolderContactos extends RecyclerView.ViewHolder {

        TextView textView;
        EditText editText;
        Button button;

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

            textView = (TextView) itemView.findViewById(R.id.textView);
            editText = (EditText) itemView.findViewById(R.id.editText);
            button = (TextView) itemView.findViewById(R.id.button);

        }

. . .

Then in the onBindViewHolder is where you are going to assign the properties or alterations to the view:

. . .

    // Esta clase es la que se llamara cada vez que notifiques un cambio
    @Override
    public void onBindViewHolder(@NonNull AdapterRecycler.ViewHolderContactos holder, int position) {
       Datos item = items.get(position);
       holder.textView.setText(item.nombre);
       holder.editText.setText(item.apellido);
       holder.button.setText(item.telefono);
    }

. . .

Then, every time you notify a change, it will be called onBindViewHolder where the data pertaining to the item will be set again, clearing the DirtyView .

0 votes

This is what I needed, thank you very much!

2voto

GMM Points 158

You need to update the range of items from the deleted item to the final one.

void eliminarItem(int index) {
    mListaContactos.remove(index);
    notifyItemRemoved(index);
    notifyItemRangeChanged(index, mListaContactos.size());
}

0 votes

Thanks for your answer, I tried your method, but it still does the same thing, I never found the way back to the RV, it is as if it did not update the data well, the only thing that worked for me is to reset the adapter to the RV, but that causes a flicker on the screen.

0 votes

In the onBindViewHolder you do not update the views?

0 votes

This method is empty, what should I do there, maybe it is the solution :O

HolaDevs.com

HolaDevs is an online community of programmers and software lovers.
You can check other people responses or create a new question if you don't find a solution

Powered by:

X