Android RecyclerView dodawanie i usuwanie elementów


144

Mam RecyclerView z polem tekstowym TextView i krzyżowym przyciskiem ImageView. Mam przycisk poza widokiem recyklingu, który sprawia, że ​​przycisk krzyżowy ImageView jest widoczny / zniknął.

Chcę usunąć element z widoku ponownego, po naciśnięciu przycisku krzyżowego elementów ImageView.

Mój 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);
        }
    }
}

Mój układ to:

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

Jak mogę uzyskać coś takiego jak onClick działający dla mojego crossButton ImageView? Czy jest lepszy sposób? Może zmiana całego przedmiotu jednym kliknięciem w usunięcie elementu? Widok recyklingu przedstawia listę lokalizacji, które należy edytować. Wszelkie porady techniczne lub komentarze / sugestie dotyczące najlepszego wdrożenia będą bardzo mile widziane.


Zobacz LINK 1 LINK 2 może pomóc U
SaravanaRaja


1
Możesz zobaczyć ten przykład w kodzie Github Happy !!!!
Cabezas

Odpowiedzi:


267

Zrobiłem coś podobnego. W twoim 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());
}

Mam nadzieję że to pomoże.

Edytować:

getPosition()jest teraz przestarzała, użyj getAdapterPosition()zamiast tego.


50
będziesz również chciał dodać notifyItemRangeChanged(getPosition, mDataSet.size());do odpowiedzi paradite po usunięciu elementu, aby wszystkie widoki poniżej usuniętego elementu odpowiednio się dopasowały.
Chris Angell

3
mam ten sam problem, ale getPosition()teraz jest on przestarzały
chip

4
Dzięki, najwyraźniej samo dzwonienie notifyItemRemoved(position)nie wystarczyło.
Ektos974

3
@chip Użyj getAdapterPosition ()
Tyler Pfaff

2
ten kod nie rozwiązuje problemu, gdy szybko usuniesz elementy zarówno z góry, jak iz dołu.
Vito Valov

61

przede wszystkim należy usunąć pozycję z listy!

  mDataSet.remove(getAdapterPosition());

następnie:

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

2
bardzo dziękuję, przypuśćmy, że muszę dodać więcej wartości, co oznacza, co mogę z tym zrobić.
MohanRaj S,

jest to związane z wielkością listy w twoim adapterze. @ Hammad Nasir
Zahra.HY

Zrobiłem to samo, ale wysokość widoku recyklingu nie zmieniła się dynamicznie. mNotes.remove (pozycja); notifyItemRemoved (pozycja); notifyItemRangeChanged (stanowisko, getItemCount ()); @ Zahra.HY nie przegapiłem niczego.
Hitesh Dhamshaniya

22

jeśli nadal przedmiot nie został usunięty, użyj tej magicznej metody :)

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

Wersja Kotlin

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

@Mostafa: Załóżmy, że mam 10 elementów i usuwam ostatni, w tym scenariuszu holder.itemView.setVisibility (View.GONE), Hide all data,
Hitesh Dhamshaniya

@HiteshDhamshaniya no it just hide and gone the last view
Mostafa Anter

Nie musisz ukrywać widoku. dataSet.remove (pozycja) działa dobrze, ale musisz wywołać notifyDatasetChamged () lub notifyItemRemoved (dataset.size () - 1)
Karue Benson Karue

dzięki dla holder.itemView.setVisibility (View.GONE);
K.Hayoev

10

Problem

RecyclerViewjest domyślnie nieświadomy zmian w Twoim zbiorze danych. Oznacza to, że za każdym razem, gdy usuniesz / dodasz do swojej listy danych, zmiany te nie zostaną odzwierciedlone bezpośrednio w RecyclerView.

Rozwiązanie

RecyclerView oferuje kilka metod udostępniania stanu zbioru danych. Dzieje się tak, ponieważ modyfikacja zbioru danych nie oznacza zmiany widoku i na odwrót. Standardowe API dla Androida pozwalają powiązać proces usuwania danych (na potrzeby pytania) z procesem usuwania widoku.

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

Najlepsze podejście

Jeśli nie określisz prawidłowo, co dzieje się podczas dodawania / usuwania elementów (mówienie o widokach), elementy podrzędne RecyclerView są animowane bez odpowiedzi z powodu braku informacji o tym, jak przesuwać różne widoki na liście.

Zamiast tego poniższy kod precyzyjnie odtworzy animację, tylko na usuwanym dziecku (I na marginesie, dla mnie naprawił wszystkie IndexOutOfBoundExceptions, oznaczone przez ślad stosu jako "niespójność danych")

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

Pod maską, jeśli przyjrzymy się RecyclerView, możemy znaleźć dokumentację wyjaśniającą, że drugim parametrem, który musimy przekazać, jest liczba pozycji, które są usuwane ze zbioru danych, a nie całkowita liczba pozycji (jak błędnie stwierdzono w niektórych innych źródłach informacji).

    /**
     * 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

Prawdopodobnie podwójna odpowiedź, ale dla mnie całkiem przydatna. Możesz zaimplementować metodę podaną poniżej RecyclerView.Adapter<RecyclerView.ViewHolder> i możesz użyć tej metody zgodnie ze swoimi wymaganiami, mam nadzieję, że zadziała dla Ciebie

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

5

Oto kilka wizualnych przykładów uzupełniających. Zobacz moją pełniejszą odpowiedź na przykłady dodawania i usuwania zakresu.

Dodaj pojedynczy element

Dodaj „Pig” w indeksie 2.

Wstaw pojedynczy element

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

Usuń pojedynczy element

Usuń „Pig” z listy.

Usuń pojedynczy element

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

4

Wypróbowałem wszystkie powyższe odpowiedzi, ale wstawianie lub usuwanie elementów do recyclinglerview powoduje problem z pozycją w dataSet. Skończyło się na użyciu delete(getAdapterPosition());wewnątrz elementu viewHolder, który świetnie sprawdzał się w znajdowaniu pozycji przedmiotów.


1
Wystarczy ustawić detektor i użyć metody getAdapterPosition () wewnątrz uchwytu widoku. Zwróci pozycję elementu.
Arun

4

Problem, jaki miałem, polegał na tym, że usuwałem element z listy, który nie był już powiązany z adapterem, aby upewnić się, że modyfikujesz właściwy adapter, możesz zaimplementować taką metodę w swoim adapterze:

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

I nazwij to w swoim fragmencie lub działaniu w ten sposób:

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

mam nadzieję, że to ci pomoże.


1

jeśli chcesz usunąć przedmiot, powinieneś to zrobić: najpierw usuń przedmiot:

phones.remove(position);

w następnym kroku należy powiadomić adapter recyklera o usunięciu elementu za pomocą tego kodu:

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

ale jeśli zmienisz element, zrób to: najpierw zmień parametr swojego obiektu w ten sposób:

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

lub nowy w ten sposób:

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

następnie powiadom swój adapter recyklera, że ​​modyfikujesz element za pomocą tego kodu:

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

nadzieja pomaga.


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

To naprawdę zły pomysł, ponieważ wymusisz pełne ponowne losowanie przedmiotów, z prawdopodobnie zauważalnym opóźnieniem, jeśli masz dużo przedmiotów. Należy zadzwonić notifyItemRemoved(position).
Minas Mina

ya poprawne, używamy również notifyItemRemoved (pozycja) instated of notifyDataSetChanged ()
Tarun Umath

1

Metoda onBindViewHolderNapisz ten kod

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 Każdy chce zaimplementować coś takiego w klasie Main zamiast w klasie Adapter, możesz użyć:

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

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

gdzie friendsListRecycler to nazwa adaptera


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

przekształcić interfejs w niestandardową klasę adaptera i obsługiwać zdarzenie kliknięcia w widoku recyklera.

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

po zdefiniowaniu adaptera i powiązaniu z widokiem recyklera o nazwie poniższy kod.

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

0

W przypadku, gdy zastanawiasz się, tak jak ja, gdzie możemy znaleźć pozycję adaptera w metodzie getadapterposition (); jest w obiekcie viewholder. więc musisz umieścić swój kod w ten sposób

mdataset.remove(holder.getadapterposition());

0

W działalności:

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

W twoim adapterze:

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);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.