Android RecyclerView Hinzufügen und Entfernen von Elementen

144

Ich habe eine RecyclerView mit einem TextView-Textfeld und einer Kreuzschaltfläche ImageView. Ich habe eine Schaltfläche außerhalb der Recycling-Ansicht, die die Kreuzschaltfläche ImageView sichtbar / verschwunden macht.

Ich möchte ein Element aus der Recylerview entfernen, wenn diese Elemente die Schaltfläche ImageView drücken.

Mein Adapter:

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);
        }
    }
}

Mein Layout ist:

<?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>

Wie kann ich so etwas wie einen onClick für mein crossButton ImageView zum Laufen bringen? Gibt es einen besseren Weg? Vielleicht ändern Sie den gesamten Artikel per Klick in einen Artikel entfernen? Die Recycling-Ansicht zeigt eine Liste der Speicherorte, die bearbeitet werden müssen. Alle technischen Ratschläge oder Kommentare / Vorschläge zur besten Implementierung wären sehr dankbar.

2100
quelle
Werfen Sie einen Blick LINK 1 LINK 2 es kann U
SaravanaRaja
1
Sie können dieses Beispiel in Github Happy Code sehen !!!!
Cabezas

Antworten:

266

Ich habe etwas ähnliches gemacht. In Ihrem 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());
}

Hoffe das hilft.

Bearbeiten:

getPosition()ist jetzt veraltet, verwenden Sie getAdapterPosition()stattdessen.

Paradit
quelle
50
Sie möchten auch notifyItemRangeChanged(getPosition, mDataSet.size());die Antwort von paradite hinzufügen , nachdem Sie das Element entfernt haben, damit alle Ansichten unter dem entfernten Element entsprechend angepasst werden.
Chris Angell
3
Ich habe das gleiche Problem, getPosition()ist aber jetzt veraltet
Chip
4
Danke, anscheinend notifyItemRemoved(position)war es nicht genug , nur anzurufen .
Ektos974
3
@chip Verwenden Sie getAdapterPosition ()
Tyler Pfaff
2
Dieser Code behebt kein Problem, wenn Sie Elemente schnell von oben und unten entfernen.
Vito Valov
61

Zunächst sollte das Element aus der Liste entfernt werden!

  mDataSet.remove(getAdapterPosition());

dann:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());
Zahra.HY
quelle
2
Angenommen, ich muss mehr Werte hinzufügen, was kann ich dafür tun?
MohanRaj S
es hängt mit der Größe der Liste in Ihrem Adapter zusammen. @ Hammad Nasir
Zahra.HY
Ich habe das Gleiche getan, aber die Höhe der Recyclingansicht wurde nicht dynamisch geändert. mNotes.remove (Position); notifyItemRemoved (Position); notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY habe ich etwas verpasst.
Hitesh Dhamshaniya
22

Wenn der Gegenstand immer noch nicht entfernt wurde, benutze diese magische Methode :)

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

Kotlin-Version

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}
Mostafa Anter
quelle
@Mostafa: Angenommen, ich habe 10 Elemente und entferne das letzte in diesem Szenario. Holder.itemView.setVisibility (View.GONE), Alle Daten ausblenden,
Hitesh Dhamshaniya
@HiteshDhamshaniya nein es nur verstecken und die letzte Ansicht gegangen
Mostafa Anter
Die Ansicht muss nicht ausgeblendet werden. dataSet.remove (position) funktioniert einwandfrei, aber Sie müssen notifyDatasetChamged () oder notifyItemRemoved (dataset.size () - 1)
aufrufen
danke für Inhaber.itemView.setVisibility (View.GONE);
K. Hayoev
10

Problem

RecyclerViewist standardmäßig nicht über Ihre Dataset-Änderungen informiert. Dies bedeutet, dass diese Änderungen bei jeder Löschung / Hinzufügung in Ihrer Datenliste nicht direkt in Ihrem RecyclerView übernommen werden.

Lösung

RecyclerView bietet Ihnen einige Methoden zum Freigeben Ihres Dataset-Status. Dies liegt daran, dass eine Änderung an Ihrem Dataset keine Ansichtsänderung impliziert und umgekehrt. Mit den Standard-Android-APIs können Sie den Prozess der Datenentfernung (zum Zweck der Frage) mit dem Prozess der Ansichtsentfernung verknüpfen.

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

Bester Ansatz

Wenn Sie nicht richtig angeben, was beim Hinzufügen / Entfernen von Elementen geschieht (über Ansichten sprechen), werden RecyclerView-Kinder aufgrund fehlender Informationen zum Verschieben der verschiedenen Ansichten in der Liste nicht reagiert.

Stattdessen spielt der folgende Code die Animation genau auf dem Kind ab, das entfernt wird (und als Randnotiz hat er für mich alle IndexOutOfBoundExceptions behoben , die durch die Stapelverfolgung als "Dateninkonsistenz" gekennzeichnet sind).

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

Wenn wir unter die Haube schauen, RecyclerViewfinden wir eine Dokumentation, die erklärt, dass der zweite Parameter, den wir übergeben müssen, die Anzahl der Elemente ist, die aus dem Datensatz entfernt werden, nicht die Gesamtzahl der Elemente (wie in einigen anderen Informationsquellen falsch angegeben).

    /**
     * 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);
    }
Andrea Cioccarelli
quelle
7

Möglicherweise eine doppelte Antwort, aber sehr nützlich für mich. Sie können die unten angegebene Methode in implementieren RecyclerView.Adapter<RecyclerView.ViewHolder> und diese Methode gemäß Ihren Anforderungen verwenden. Ich hoffe, dass sie für Sie funktioniert

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

Hier sind einige visuelle Ergänzungsbeispiele. Siehe meine ausführlichere Antwort Beispiele zum Hinzufügen und Entfernen eines Bereichs.

Einzelteil hinzufügen

Fügen Sie "Schwein" am Index hinzu 2.

Einzelteil einfügen

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

Einzelteil entfernen

Entfernen Sie "Schwein" aus der Liste.

Einzelteil entfernen

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

Ich habe alle oben genannten Antworten ausprobiert, aber das Einfügen oder Entfernen von Elementen in recyclerview verursacht Probleme mit der Position im dataSet. delete(getAdapterPosition());Am Ende wurde der viewHolder verwendet, der sich hervorragend zum Finden der Position von Elementen eignet.

Arun
quelle
1
Legen Sie einfach einen Listener fest und verwenden Sie die Methode getAdapterPosition () im Ansichtshalter. Es wird die Position des Elements zurückgegeben.
Arun
4

Das Problem, das ich hatte, war, dass ich ein Element aus der Liste entfernt habe, das nicht mehr mit dem Adapter verknüpft war, um sicherzustellen, dass Sie den richtigen Adapter ändern. Sie können eine Methode wie diese in Ihrem Adapter implementieren:

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

Und nennen Sie es in Ihrem Fragment oder Ihrer Aktivität so:

adapter.removeItemAtPosition(position);
Horatio
quelle
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();
}}      

Ich hoffe das hilft dir.

Mohamed Ben Romdhane
quelle
1

Wenn Sie ein Element entfernen möchten, sollten Sie Folgendes tun: Entfernen Sie zuerst das Element:

phones.remove(position);

Im nächsten Schritt sollten Sie Ihren Recycler-Adapter darüber informieren, dass Sie einen Artikel mit diesem Code entfernen:

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

Wenn Sie jedoch ein Element ändern, gehen Sie folgendermaßen vor: Ändern Sie zuerst einen Parameter Ihres Objekts wie folgt:

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

oder neu so:

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

Benachrichtigen Sie dann Ihren Recycler-Adapter, dass Sie einen Artikel mit diesem Code ändern:

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

Hoffnung hilft dir.

Sagte Mohammad Amin Emrani
quelle
1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();
Tarun Umath
quelle
Dies ist eine wirklich schlechte Idee, da Sie eine vollständige Neuzeichnung der Elemente erzwingen, mit wahrscheinlich spürbarer Verzögerung, wenn Sie viele Elemente haben. Das Richtige ist anzurufen notifyItemRemoved(position).
Minas Mina
ya Ihr korrektes wir verwenden auch notifyItemRemoved (Position) von notifyDataSetChanged ()
Tarun Umath
1

Zur Methode onBindViewHolderSchreiben Sie diesen Code

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();
            }
        });
Diako Hasani
quelle
1

Falls jemand so etwas in der Hauptklasse anstelle der Adapterklasse implementieren möchte, können Sie Folgendes verwenden:

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

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

Dabei ist friendsListRecycler der Adaptername

Wald Baba
quelle
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();
                }
            });
Roshan Posakya
quelle
0

Schnittstelle in benutzerdefinierte Adapterklasse umwandeln und Klickereignis in der Recycler-Ansicht behandeln.

 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));
        }
    });
}

Nachdem Sie den Adapter definiert und in die Recycler-Ansicht mit dem folgenden Code gebunden haben.

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

Falls Sie sich wie ich fragen, wo können wir die Adapterposition in der Methode getadapterposition () finden? Es befindet sich in viewholder object.so müssen Sie Ihren Code so einfügen

mdataset.remove(holder.getadapterposition());
Raj Kavadia
quelle
0

In der Aktivität:

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

In Ihrem Adapter:

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);
}
Huy Nguyen
quelle