Jak zaktualizować / odświeżyć określony element w RecyclerView

112

Próbuję odświeżyć określony element w RecyclerView.

Historia: Za każdym razem, gdy użytkownik kliknie przedmiot, zostanie wyświetlony AlertDialog. Użytkownik może wpisać tekst, klikając przycisk OK. Chcę pokazać ten tekst w tej pozycji i pokazać niewidoczny ImageView- zadeklarowany w XML i adapter ViewHolder-

Użyłem tej funkcji w AlertDialogPositive Button, aby zaktualizować element:

private void updateListItem(int position) {
  View view = layoutManager.findViewByPosition(position);
  ImageView medicineSelected = (ImageView) view.findViewById(R.id.medicine_selected);
  medicineSelected.setVisibility(View.VISIBLE);
  TextView orderQuantity = (TextView) view.findViewById(R.id.order_quantity);
  orderQuantity.setVisibility(View.VISIBLE);
  orderQuantity.setText(quantity + " packet added!");

  medicinesArrayAdapter.notifyItemChanged(position);
}

Ale ten kod nie tylko zmienia itemView na przekazanej pozycji, ale także zmienia niektóre inne itemView (s)!

Jak poprawnie zmienić konkretny itemView, klikając go?

Elgendy
źródło

Odpowiedzi:

98

Można użyć notifyItemChanged(int position)metody z klasy RecyclerView.Adapter. Z dokumentacji:

Powiadom zarejestrowanych obserwatorów, że pozycja na pozycji uległa zmianie. Równoważne z wywołaniem notifyItemChanged (pozycja, null) ;.

To jest zdarzenie zmiany pozycji, a nie zdarzenie zmiany strukturalnej. Wskazuje, że jakiekolwiek odzwierciedlenie danych w pozycji jest nieaktualne i powinno zostać zaktualizowane. Przedmiot na pozycji zachowuje tę samą tożsamość.

Skoro już masz stanowisko, to powinno Ci zadziałać.

Edson Menegatti
źródło
9
Już go użyłem, ale jeśli chcę zaktualizować pozycję 0, aktualizuje pozycję 0, 9, 19, ...
Elgendy
Przepraszamy, nie zrozumiałem Twojego komentarza. Która pozycja jest aktualizowana? 0, 9 czy zakres od 0 do 9?
Edson Menegatti
Nie, to nie jest zakres, za każdym razem, gdy aktualizuję przedmiot, odświeża również niechciane elementy
Elgendy
@Learner Kiedy zaktualizujesz ViewHolder na pozycji 0, pozostaje on zaktualizowany. RecyclerView ponownie przetworzy ten element ViewHolder i użyje go na pozycji 9, więc zobaczysz tam te zmiany. Zamiast tego zalecam ustawienie tego tekstu w onBindViewHolder () - w ten sposób za każdym razem, gdy ViewHolder zostanie przetworzony, możesz sprawdzić jego położenie i ustawić tekst lub sprawić, by obraz był widoczny lub cokolwiek innego.
Cliabhach,
@Cliabhach wciąż ten sam problem robi zmiany wonBindViewHolder()
Choletski
39

Zaktualizuj pojedynczy element

  1. Zaktualizuj element danych
  2. Powiadom adapter o zmianie za pomocą notifyItemChanged(updateIndex)

Przykład

Zmień pozycję „Owce”, tak aby brzmiała „Lubię owce”.

Zaktualizuj pojedynczy element

String newValue = "I like sheep.";
int updateIndex = 3;
data.set(updateIndex, newValue);
adapter.notifyItemChanged(updateIndex);

Moja pełna odpowiedź z większą liczbą przykładów znajduje się tutaj .

Suragch
źródło
jak to zrobić, jeśli chcę, aby zaktualizowane dane przełączyły się na górę
Choy
@Choy, po zaktualizowaniu danych możesz przenieść je do indeksu 0. Zobacz sekcję Przenieś pojedynczy element tego elementu
Suragch
11

Dodaj zmieniony tekst do listy danych modelu

mdata.get(position).setSuborderStatusId("5");
mdata.get(position).setSuborderStatus("cancelled");
notifyItemChanged(position);
Shamir Kp
źródło
10

Myślę, że mam pomysł, jak sobie z tym poradzić. Aktualizacja jest taka sama, jak usuwanie i zastępowanie w dokładnym miejscu. Więc najpierw usuwam element z tej pozycji, używając poniższego kodu:

public void removeItem(int position){
    mData.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mData.size());
}

a następnie dodałbym przedmiot w tej konkretnej pozycji, jak pokazano poniżej:

public void addItem(int position, Landscape landscape){
    mData.add(position, landscape);
    notifyItemInserted(position);
    notifyItemRangeChanged(position, mData.size());
}

Próbuję to teraz wdrożyć. Dałbym ci informację zwrotną, kiedy skończę!

EGBENCHONG II J. Ayuk
źródło
2
Robisz jeden dodatkowy krok. Po prostu zamień element na pozycji i wywołaj element notifyItemChanged (pozycja);
toshkinl
@toshkinl Skąd możesz wiedzieć, że dane pochodzą z JSONObject? bez pozycji?
olajide
7

Musiałem rozwiązać ten problem, przechwytując pozycję elementu, który wymagał modyfikacji, a następnie w wywołaniu adaptera

public void refreshBlockOverlay(int position) {
    notifyItemChanged(position);
}

, spowoduje to wywołanie onBindViewHolder (posiadacz ViewHolder, pozycja int) dla tego konkretnego elementu w tej konkretnej pozycji.

haneenCo
źródło
2

Poniższe rozwiązanie zadziałało dla mnie:

W elemencie RecyclerView użytkownik kliknie przycisk, ale inny widok, taki jak TextView, zostanie zaktualizowany bez bezpośredniego powiadamiania adaptera:

Znalazłem dobre rozwiązanie bez użycia metody notifyDataSetChanged (), ta metoda przeładowuje wszystkie dane z recyklingu, więc jeśli masz obraz lub wideo w elemencie, zostaną one ponownie załadowane, a wrażenia użytkownika nie będą dobre:

Oto przykład kliknięcia ikony ImageView podobnej do ikony i zaktualizowania tylko jednego widoku TextView (można zaktualizować więcej widoku w ten sam sposób dla tego samego elementu), aby wyświetlić aktualizację licznika po dodaniu +1:

// View holder class inside adapter class
public class MyViewHolder extends RecyclerView.ViewHolder{

    ImageView imageViewLike;

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

        imageViewLike = itemView.findViewById(R.id.imageViewLike);
        imageViewLike.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int pos = getAdapterPosition(); // Get clicked item position
                TextView tv = v.getRootView().findViewById(R.id.textViewLikeCount); // Find textView of recyclerView item
                resultList.get(pos).setLike(resultList.get(pos).getLike() + 1); // Need to change data list to show updated data again after scrolling
                tv.setText(String.valueOf(resultList.get(pos).getLike())); // Set data to TextView from updated list
            }
        });
    }
Touhid
źródło
1

Sposobem, który zadziałał dla mnie osobiście, jest użycie metod adaptera recyclinglerview do radzenia sobie ze zmianami w jego elementach.

Mogłoby to wyglądać podobnie do tego, utworzyć metodę w widoku niestandardowego recyklera, mniej więcej taką:

public void modifyItem(final int position, final Model model) {
    mainModel.set(position, model);
    notifyItemChanged(position);
}
user3709410
źródło
0

W swojej klasie adaptera, w metodzie onBindViewHolder, ustaw ViewHolder na setIsRecyclable (false) jak w poniższym kodzie.

@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder p1, int p2)
{
    // TODO: Implement this method
    p1.setIsRecyclable(false);

    // Then your other codes
}
shankar_vl
źródło
0

wystarczy dodać następujący kod w oknie dialogowym ostrzeżenia po kliknięciu przycisku Zapisz

          recyclerData.add(position, updateText.getText().toString());
          recyclerAdapter.notifyItemChanged(position);
Dhruvisha
źródło
0

Problem polega na tym, RecyclerView.Adatperże nie ma żadnych metod, które zwracają indeks elementu

public abstract static class Adapter<VH extends ViewHolder> {
  /**
   * returns index of the given element in adapter, return -1 if not exist
   */
  public int indexOf(Object elem);
}

Moje obejście polega na utworzeniu instancji mapy dla (element, pozycja) s

public class FriendAdapter extends RecyclerView.Adapter<MyViewHolder> {
  private Map<Friend, Integer> posMap ;
  private List<Friend> friends;

  public FriendAdapter(List<Friend> friends ) {
    this.friends = new ArrayList<>(friends);
    this.posMap = new HashMap<>();
    for(int i = 0; i < this.friends.size(); i++) {
      posMap.put(this.friends.get(i), i);
    }
  }

  public int indexOf(Friend friend) {
    Integer position = this.posMap.get(elem);
    return position == null ? -1 : position;
  }
  // skip other methods in class Adapter
}
  • typ elementu (tutaj klasa Friend) powinien implementować hashCode()i equals()ponieważ jest kluczowy w hashmap.

kiedy element się zmienił,

  void someMethod() {
    Friend friend = ...;
    friend.setPhoneNumber('xxxxx');

    int position = friendAdapter.indexOf(friend);
    friendAdapter.notifyItemChanged(position);

  }

Dobrze jest zdefiniować metodę pomocniczą

public class FriendAdapter extends extends RecyclerView.Adapter<MyViewHolder> {

  public void friendUpdated(Friend friend) {
    int position = this.indexOf(friend);
    this.notifyItemChanged(position);
  }
}

Instancja mapy ( Map<Friend, Integer> posMap) niekoniecznie jest wymagana. Jeśli mapa nie jest używana, pętla po całej liście może znaleźć pozycję elementu.

chmin
źródło
-1

To także mój ostatni problem. Tutaj moje rozwiązanie używam modelu danych i adaptera dla mojego RecyclerView

 /*Firstly, register your new data to your model*/
 DataModel detail = new DataModel(id, name, sat, image);

 /*after that, use set to replace old value with the new one*/
 int index = 4;
 mData.set(index, detail);

 /*finally, refresh your adapter*/
 if(adapter!=null)
    adapter.notifyItemChanged(index);
deya tri
źródło
-1

jeśli tworzysz jeden obiekt i dodajesz go do listy, której używasz w adapterze, gdy zmieniasz jeden element listy w adapterze, wszystkie inne elementy również się zmieniają, innymi słowy, wszystko zależy od odniesień, a twoja lista nie przechowywać oddzielne kopie tego pojedynczego obiektu.

A.sobhdel
źródło
-5

W adapterze RecyclerView powinieneś mieć ArrayList oraz jedną metodę addItemsToList(items)dodawania elementów listy do ArrayList. Następnie możesz dodawać elementy listy, dzwoniąc adapter.addItemsToList(items)dynamicznie. Po dodaniu wszystkich elementów listy do ArrayList możesz zadzwonićadapter.notifyDataSetChanged() aby wyświetlić listę.

Możesz użyć notifyDataSetChangedw adapterze dla RecyclerView

Nooruddin Lakhani
źródło
tak mam ArrayList na dane, działa bez problemu, mój problem z aktualizacją konkretnej -istniejącej- pozycji na tej liście
Elgendy
powinieneś mieć aktualizowaną pozycję pozycji, a następnie wystarczy umieścić nową pozycję na jej odpowiedniej pozycji, np. mList.add (pozycja) .setName ("nowa nazwa"); następnie użyj mAdapter.notifyDataSetChanged ();
Nooruddin Lakhani
4
Ta odpowiedź ignoruje chęć zaktualizowania pojedynczego elementu. Wywołanie notifyDataSetChanged () powinno być wykonywane tylko wtedy, gdy zmieni się cały zestaw danych.
Mark McClelland