Recycler Android Visualizza l'aggiunta e la rimozione di oggetti


144

Ho un RecyclerView con una casella di testo TextView e un pulsante incrociato ImageView. Ho un pulsante al di fuori del recyclerview che rende visibile / scomparso il pulsante a croce ImageView.

Sto cercando di rimuovere un elemento dal recylerview, quando si preme il pulsante a croce ImageView.

Il mio adattatore:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {

    private ArrayList<String> mDataset;
    private static Context sContext;

    public MyAdapter(Context context, ArrayList<String> myDataset) {
        mDataset = myDataset;
        sContext = context;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);

        ViewHolder holder = new ViewHolder(v);
        holder.mNameTextView.setOnClickListener(MyAdapter.this);
        holder.mNameTextView.setOnLongClickListener(MyAdapter.this);

        holder.mNameTextView.setTag(holder);

        return holder;
    }

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

        holder.mNameTextView.setText(mDataset.get(position));

    }

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


    @Override
    public void onClick(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (view.getId() == holder.mNameTextView.getId()) {
            Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
        }
    }


    @Override
    public boolean onLongClick(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (view.getId() == holder.mNameTextView.getId()) {
            mDataset.remove(holder.getPosition());

            notifyDataSetChanged();

            Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
                    Toast.LENGTH_SHORT).show();
        }
        return false;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mNumberRowTextView;
        public TextView mNameTextView;


        public ViewHolder(View v) {
            super(v);

            mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
        }
    }
}

Il mio layout è:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:id="@+id/layout">

    <TextView
        android:id="@+id/nameTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:padding="5dp"
        android:background="@drawable/greyline"/>

    <ImageView
        android:id="@+id/crossButton"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:visibility="gone"
        android:layout_marginLeft="50dp"
        android:src="@drawable/cross" />
</LinearLayout>

Come posso ottenere qualcosa come un onClick che funziona con il mio crossButton ImageView? C'è un modo migliore? Forse cambiando l'intero elemento onclick in un rimuovere l'elemento? Il recyclerview mostra un elenco di posizioni che devono essere modificate. Qualsiasi consiglio tecnico o commento / suggerimento sulla migliore implementazione sarebbe molto apprezzato.


Dai un'occhiata a LINK 1 LINK 2 può aiutare U
SaravanaRaja,


1
Puoi vedere questo esempio in Github Happy code !!!!
Cabezas,

Risposte:


267

Ho fatto qualcosa di simile. Nel tuo MyAdapter:

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    public CardView mCardView;
    public TextView mTextViewTitle;
    public TextView mTextViewContent;
    public ImageView mImageViewContentPic;

    public ImageView imgViewRemoveIcon;
    public ViewHolder(View v) {
        super(v);
        mCardView = (CardView) v.findViewById(R.id.card_view);
        mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
        mTextViewContent = (TextView) v.findViewById(R.id.item_content);
        mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
        //......
        imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);

        mTextViewContent.setOnClickListener(this);
        imgViewRemoveIcon.setOnClickListener(this);
        v.setOnClickListener(this);
        mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                if (mItemClickListener != null) {
                    mItemClickListener.onItemClick(view, getPosition());
                }
                return false;
            }
        });
    }


    @Override
    public void onClick(View v) {
        //Log.d("View: ", v.toString());
        //Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
        if(v.equals(imgViewRemoveIcon)){
            removeAt(getPosition());
        }else if (mItemClickListener != null) {
            mItemClickListener.onItemClick(v, getPosition());
        }
    }
}

public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
    this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
    mDataset.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mDataSet.size());
}

Spero che questo ti aiuti.

Modificare:

getPosition()è deprecato ora, utilizzare getAdapterPosition()invece.


50
vorrai anche aggiungere notifyItemRangeChanged(getPosition, mDataSet.size());alla risposta di paradite dopo aver rimosso l'elemento in modo che tutte le viste sottostanti l'elemento rimosso si adeguino di conseguenza.
Chris Angell,

3
ho lo stesso problema ma getPosition()ora è deprecato
chip

4
Grazie, apparentemente solo chiamare notifyItemRemoved(position)non era abbastanza.
Ektos974,

3
@chip Usa getAdapterPosition ()
Tyler Pfaff il

2
questo codice non risolve un problema quando si rimuovono rapidamente gli elementi sia dall'alto che dal basso.
Vito Valov,

60

prima di tutto, l'elemento dovrebbe essere rimosso dalla lista!

  mDataSet.remove(getAdapterPosition());

poi:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());

2
grazie mille, supponiamo che devo aggiungere più valori significa cosa posso fare per quello.
MohanRaj S

è legato alla dimensione dell'elenco nel tuo adattatore. @ Hammad Nasir
Zahra.HY

Ho fatto lo stesso, ma l'altezza di riciclo non è cambiata dinamicamente. mNotes.remove (posizione); notifyItemRemoved (posizione); notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY ho perso qualcosa.
Hitesh Dhamshaniya,

22

se l'oggetto non è stato rimosso, usa questo metodo magico :)

private void deleteItem(int position) {
        mDataSet.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, mDataSet.size());
        holder.itemView.setVisibility(View.GONE);
}

Versione di Kotlin

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}

@Mostafa: Supponiamo di avere 10 elementi e di rimuovere l'ultimo, in quello scenario holder.itemView.setVisibility (View.GONE), Nascondi tutti i dati,
Hitesh Dhamshaniya,

@HiteshDhamshaniya no nascondeva e se ne andava l'ultima vista
Mostafa Anter,

Non è necessario nascondere la vista. dataSet.remove (position) funziona bene, ma devi chiamare notificationDatasetChamged () o notifyItemRemoved (dataset.size () - 1)
Karue Benson Karue

grazie per holder.itemView.setVisibility (View.GONE);
K.Hayoev l'

10

Problema

RecyclerViewè, per impostazione predefinita, ignaro delle modifiche al set di dati. Ciò significa che ogni volta che effettui una cancellazione / aggiunta nel tuo elenco di dati, tali modifiche non verranno riflesse direttamente su RecyclerView.

Soluzione

RecyclerView offre alcuni metodi per condividere lo stato del set di dati. Ciò accade perché una modifica del set di dati non implica una modifica della vista e viceversa. Le API Android standard consentono di associare il processo di rimozione dei dati (ai fini della domanda) con il processo di rimozione della vista.

notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)

Il miglior approccio

Se non specifichi correttamente cosa succede durante l'aggiunta / rimozione di elementi (parlando di viste), i bambini di RecyclerView vengono animati senza risposta a causa della mancanza di informazioni su come spostare le diverse viste nell'elenco.

Al contrario, il codice seguente riprodurrà esattamente l'animazione, proprio sul figlio che viene rimosso (E come nota a IndexOutOfBoundExceptionmargine , per me ha corretto qualsiasi s, contrassegnato dallo stacktrace come "incoerenza dei dati")

void remove(position: Int) {
    dataset.removeAt(position)
    notifyItemChanged(position)
    notifyItemRangeRemoved(position, 1)
}

Sotto il cofano, se esaminiamo RecyclerViewpossiamo trovare la documentazione che spiega che il secondo parametro che dobbiamo passare è il numero di elementi che vengono rimossi dal set di dati, non il numero totale di elementi (Come erroneamente affermato in alcune altre fonti di informazioni).

    /**
     * Notify any registered observers that the <code>itemCount</code> items previously
     * located at <code>positionStart</code> have been removed from the data set. The items
     * previously located at and after <code>positionStart + itemCount</code> may now be found
     * at <code>oldPosition - itemCount</code>.
     *
     * <p>This is a structural change event. Representations of other existing items in the data
     * set are still considered up to date and will not be rebound, though their positions
     * may be altered.</p>
     *
     * @param positionStart Previous position of the first item that was removed
     * @param itemCount Number of items removed from the data set
     */
    public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
        mObservable.notifyItemRangeRemoved(positionStart, itemCount);
    }

7

Forse una risposta duplicata ma abbastanza utile per me. Puoi implementare il metodo indicato di seguito RecyclerView.Adapter<RecyclerView.ViewHolder> e puoi utilizzare questo metodo secondo i tuoi requisiti, spero che funzionerà per te

public void removeItem(@NonNull Object object) {
        mDataSetList.remove(object);
        notifyDataSetChanged();
    }

5

Ecco alcuni esempi visivi supplementari. Vedi la mia risposta più completa per esempi di aggiunta e rimozione di un intervallo.

Aggiungi singolo elemento

Aggiungi "Maiale" all'indice 2.

Inserisci un singolo articolo

String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);

Rimuovi elemento singolo

Rimuovere "Pig" dall'elenco.

Rimuovi elemento singolo

int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);

4

Ho provato tutte le risposte di cui sopra, ma l'inserimento o la rimozione di elementi per riciclare la vista causa problemi con la posizione nel set di dati. delete(getAdapterPosition());Finì per usare all'interno della viewHolder che funzionava benissimo nel trovare la posizione degli oggetti.


1
Basta impostare un listener e utilizzare il metodo getAdapterPosition () all'interno del supporto della vista. Restituirà la posizione dell'articolo.
Arun,

4

Il problema che ho avuto è stato che stavo rimuovendo un elemento dall'elenco che non era più associato all'adattatore per essere sicuro che stai modificando l'adattatore corretto, puoi implementare un metodo come questo nel tuo adattatore:

public void removeItemAtPosition(int position) {
    items.remove(position);
}

E chiamalo nel tuo frammento o attività in questo modo:

adapter.removeItemAtPosition(position);

2
  public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;

public MyAdapter(Context context, List<cardview_widgets> list) {
    this.context = context;
    this.list = list;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
            viewGroup, false);
    return new MyViewHolder(view);
}

public static class MyViewHolder extends RecyclerView.ViewHolder {
    TextView txt_value;
    TextView txt_category;
    ImageView img_inorex;
    ImageView img_category;
    TextView txt_date;

    public MyViewHolder(@NonNull View itemView) {
        super(itemView);
        txt_value = itemView.findViewById(R.id.id_values);
        txt_category = itemView.findViewById(R.id.id_category);
        img_inorex = itemView.findViewById(R.id.id_inorex);
        img_category = itemView.findViewById(R.id.id_imgcategory);
        txt_date = itemView.findViewById(R.id.id_date);
    }
}

@NonNull
@Override
public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, int i) {

    myViewHolder.txt_value.setText(String.valueOf(list.get(i).getValuee()));
    myViewHolder.txt_category.setText(list.get(i).getCategory());
    myViewHolder.img_inorex.setBackgroundColor(list.get(i).getImg_inorex());
    myViewHolder.img_category.setImageResource(list.get(i).getImg_category());
    myViewHolder.txt_date.setText(list.get(i).getDate());
    myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            list.remove(myViewHolder.getAdapterPosition());
            notifyDataSetChanged();
            return false;
        }
    });
}

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

Spero che questo ti sia d'aiuto.


1

se vuoi rimuovere un articolo dovresti farlo: prima rimuovi un elemento:

phones.remove(position);

nel passaggio successivo è necessario informare l'adattatore del riciclatore che si rimuove un articolo con questo codice:

notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());

ma se modifichi un elemento fai questo: prima modifica un parametro del tuo oggetto in questo modo:

Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);

o nuovo così:

Service s = new Service();
services.set(position,s);

quindi avvisare l'adattatore per il riciclaggio che si modifica un articolo con questo codice:

notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());

la speranza ti aiuta.


1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();

Questa è una pessima idea, in quanto costringerai a ridisegnare completamente gli oggetti, con un ritardo probabilmente notevole se hai molti oggetti. La cosa giusta da fare è chiamare notifyItemRemoved(position).
Minas Mina,

ya il tuo corretto usiamo anche notifyItemRemoved (position) installato di notifyDataSetChanged ()
Tarun Umath il

1

Per metodo onBindViewHolderscrivere questo codice

holder.remove.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
                values.remove(position);
                notifyDataSetChanged();
            }
        });

1

Incase Chiunque desidera implementare qualcosa del genere nella classe Main anziché nella classe Adapter, è possibile utilizzare:

public void removeAt(int position) {
    peopleListUser.remove(position);

    friendsListRecycler.getAdapter().notifyItemRemoved(position);
    friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}

dove friendsListRecycler è il nome dell'adattatore


0
 //////// set the position
 holder.cancel.setTag(position);


///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {

            int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
            mDataset.remove(positionToRemove);
            notifyDataSetChanged();
                }
            });

0

rendere l'interfaccia nella classe dell'adattatore personalizzata e gestire l'evento click sulla vista del riciclatore.

 onItemClickListner onItemClickListner;

public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
    this.onItemClickListner = onItemClickListner;
}

public interface onItemClickListner {
    void onClick(Contact contact);//pass your object types.
}
    @Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
    // below code handle click event on recycler view item.
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            onItemClickListner.onClick(mContectList.get(position));
        }
    });
}

dopo aver definito l'adattatore e collegarlo alla vista del riciclatore chiamata sotto il codice.

        adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
        @Override
        public void onClick(Contact contact) {
            contectList.remove(contectList.get(contectList.indexOf(contact)));
            adapter.notifyDataSetChanged();
        }
    });
}

0

Nel caso ti stia chiedendo come ho fatto, dove possiamo ottenere la posizione dell'adattatore nel metodo getadapterposition (); è in viewholder object.so devi inserire il tuo codice in questo modo

mdataset.remove(holder.getadapterposition());

0

Nell'attività:

mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);

Nel tuo adattatore:

void removeAt(int position) {
    list.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, list.size());
}

void updateAt(int position, String text, Boolean completed) {
    TodoEntity todoEntity = list.get(position);
    todoEntity.setText(text);
    todoEntity.setCompleted(completed);
    notifyItemChanged(position);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.