RecyclerView.ViewHolder - getLayoutPosition vs getAdapterPosition

88

Od nowej wersji biblioteki obsługi (22.x) getPosition()metoda RecyclerView.ViewHolderklasy została wycofana w miejsce metod wymienionych w temacie. Naprawdę nie dostaję różnicy z czytania dokumentów. Czy ktoś mógłby wyjaśnić tę różnicę w kategoriach laika?

Mam następujący przypadek użycia - daję adapterowi a List, a także chcę mieć możliwość skojarzenia dodatkowych informacji dla każdego elementu listy. Mam mapowanie z pozycji na dodatkowe, a mapowanie jest dostępne dla posiadaczy, aby mogli pobrać dodatkowe dla swojej pozycji i zrobić z tym coś. W uchwycie, której metody mam użyć?

Co dzieje się z pozycjami posiadaczy, gdy pozycje listy w indeksach 0 i 1 zostaną zamienione miejscami? Co zwracają metody?

wujek
źródło

Odpowiedzi:

115

To trudna sytuacja, przepraszam, że dokumenty nie są wystarczające.

Gdy zmienia się zawartość adaptera (i wywołujesz notify***()), RecyclerView żąda nowego układu. Od tego momentu, dopóki system układu nie zdecyduje się obliczyć nowego układu (<16 ms), położenie układu i położenie adaptera mogą nie pasować, ponieważ układ nie odzwierciedla jeszcze zmian adaptera.

W twoim przypadku, ponieważ twoje dane są powiązane z zawartością twojego adaptera (i zakładam, że dane są zmieniane w tym samym czasie ze zmianami adaptera), powinieneś używać adapterPosition.

Uważaj jednak, jeśli dzwonisz notifyDataSetChanged(), ponieważ unieważnia to wszystko, RecyclerView nie wie, że pozycja adaptera ViewHolder do czasu obliczenia następnego układu. W takim przypadku getAdapterPosition()zwróci RecyclerView#NO_POSITION( -1).

Ale powiedzmy, że jeśli zadzwoniłeś notifyItemInserted(0), getAdapterPosition()ViewHolder, który był poprzednio na pozycji 0, zacznie 1natychmiast wracać . Tak długo, jak wysyłasz szczegółowe zdarzenia powiadomień, zawsze jesteś w dobrym stanie (znamy położenie adaptera, mimo że nowy układ nie został jeszcze obliczony).

Inny przykład, jeśli robisz coś po kliknięciu przez użytkownika, jeśli getAdapterPosition()zwraca NO_POSITION, najlepiej zignorować to kliknięcie, ponieważ nie wiesz, co kliknął użytkownik (chyba że masz inny mechanizm, np. Stabilne identyfikatory do wyszukiwania pozycji).

Edytuj, gdy pozycja układu jest dobra

Powiedzmy, że używasz LinearLayoutManageri chcesz uzyskać dostęp do ViewHolder nad aktualnie klikniętym elementem. W takim przypadku powinieneś użyć pozycji układu, aby uzyskać powyższy element.

mRecyclerView.findViewHolderForLayoutPosition(myViewHolder.getLayoutPosition() - 1)

Musisz użyć pozycji układu, ponieważ pasuje ona do tego, co użytkownik obecnie widzi na ekranie.

yigit
źródło
1
Trochę się pobawiłem i okazuje się, że metoda getAdapterPosition () zawsze zwraca mi -1. Debugowałem go, a powodem jest to, że kod w metodzie (ostateczny rodzic ViewParent = itemView.getParent (); if (! (Instancja nadrzędna RecyclerView)) {return -1;} zawsze dostaje się do bloku if, czyli widoku recyklera nie jest rodzicem widoku mojej komórki. Jak to możliwe? Mój kod do utworzenia posiadacza to: return MyViewHolder (LayoutInflater.from (viewGroup.getContext ()). inflate (R.layout.test_list_item, viewGroup, false)); (Ciąg dalszy w innym komentarzu.)
wujek
Kiedy zmieniam kod, aby wywołać inflate (R.layout.test_list_item, viewGroup, true); (zwróć uwagę na prawdę dla 'dołącz do roota), Android zgłasza: java.lang.IllegalStateException: określone dziecko ma już rodzica. Najpierw musisz wywołać metodę removeView () na rodzicu dziecka. Jaki jest więc prawidłowy sposób tworzenia uchwytu widoku z widokiem, który jest prawidłowo przymocowany do widoku recyklera? O dziwo, nawet jeśli getAdapterPosition () zwraca -1, ponieważ rodzic ma wartość null, wszystko inne działa dobrze.
wujek
1
Parametr logiczny w inflatorze układu to „addToParent”. Musi być fałszywa, ponieważ za jej dodanie odpowiada LayoutManager. Myślę, że wywołujesz metodę getAdapterPosition w onBind, gdzie pozycja została już przekazana. Technicznie rzecz biorąc, uchwyt widoku reprezentuje tę pozycję po zwróceniu onBind. Btw, zaktualizowaliśmy pozycję getAdapter, aby zwracała prawidłową pozycję (jeśli to możliwe), nawet jeśli jest odłączona, zostanie wkrótce wydana.
yigit
Masz rację, wzywam getAdapterPosition w onBind. Co powinienem zrobić w tym przypadku, potrzebuję pozycji, aby uzyskać informacje, które mają wpływ na stany niektórych poglądów. Czy wywołanie getLayoutPosition w tym przypadku jest prawidłowe?
wujek
5
Należy użyć parametru position, który jest przekazywany do metody onBind.
yigit
2


By wykazać różnice (S) getAdapterPosition(), getLayoutPosition()i również position; zwrócilibyśmy uwagę na poniższe przypadki:

1. positionargument w onBindViewHolder()metodzie:

Możemy użyć positiondo powiązania danych z widokiem i jest w porządku użycie positionargumentu, aby to zrobić, ale nie jest w porządku używanie positionargumentu do obsługi kliknięć użytkownika, a jeśli go użyłeś, zobaczysz ostrzeżenie "Nie traktować positionjako naprawione i użyj holder.getAdapterPosition()zamiast tego ".

2 getAdapterPosition().:

Ta metoda zawsze obejmuje zaktualizowaną pozycję adaptera holder. Oznacza to, że za każdym razem, gdy klikasz na element, pytasz adapter o to position. dzięki czemu uzyskasz najnowszą pozycję tego elementu pod względem logiki adaptera.

3 getLayoutPosition().:

Czasami konieczne jest znalezienie positionpod względem zaktualizowanego układu (ostatnio przekazanego układu, który użytkownik widzi teraz), na przykład: Jeśli użytkownik poprosi o trzeci position, może zobaczyć i używasz swipe/ dismissdla przedmiotów lub zastosuj dowolną animację lub dekoracje dla przedmiotów, które lepiej będzie użyć getLayoutPosition()zamiast getAdapterPosition(), ponieważ zawsze będziesz mieć pewność, że masz do czynienia z pozycją przedmiotów pod względem ostatnio przekazanego układu.


Aby uzyskać więcej informacji na ten temat; zobacz tutaj . . .

elyar abad
źródło