Badałem RecyclerView
i byłem zaskoczony, widząc, że RecyclerView
nie ma onItemClickListener()
.
Mam dwa pytania.
Główne pytanie
Chcę wiedzieć, dlaczego Google usunęło onItemClickListener()
?
Czy występuje problem z wydajnością lub coś innego?
Pytanie drugie
Rozwiązałem swój problem pisząc onClick
w RecyclerView.Adapter
:
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
}
@Override
public void onClick(View v) {
}
}
Czy to w porządku / czy jest jakiś lepszy sposób?
java
android
android-recyclerview
Tarun Varshney
źródło
źródło
QUESTION
Odpowiedzi:
tl; dr 2016 Użyj RxJava i PublishSubject, aby wyświetlić Observable dla kliknięć.
Oryginalny post:
Od momentu wprowadzenia
ListView
,onItemClickListener
jest problematyczne. W momencie, gdy masz odbiornik kliknięć dla któregokolwiek z elementów wewnętrznych, wywołanie zwrotne nie zostanie uruchomione, ale nie zostanie powiadomione ani dobrze udokumentowane (jeśli w ogóle), więc było wiele zamieszania i SO pytań na ten temat.Biorąc pod uwagę, że
RecyclerView
idzie o krok dalej i nie ma koncepcji wiersza / kolumny, ale raczej arbitralnie rozmieszczoną liczbę dzieci, delegowali onClick do każdego z nich lub do implementacji programisty.Myśleć
Recyclerview
nie jakoListView
1: 1 wymiany, ale raczej jako bardziej elastyczny komponent do skomplikowanych przypadków użycia. I jak mówisz, Twoje rozwiązanie jest tym, czego Google oczekuje od ciebie. Teraz masz adapter, który może delegować onClick do interfejsu przekazanego konstruktorowi, który jest prawidłowym wzorcem zarówno dla, jakListView
i dlaRecyclerview
.a następnie na adapterze
Teraz spójrz na ten ostatni fragment kodu:
onCreateViewHolder(ViewGroup parent, int viewType)
podpis sugeruje już różne typy widoków. Dla każdego z nich będziesz również potrzebować innego widoku, a następnie każdy z nich może mieć inny zestaw kliknięć. Możesz też po prostu utworzyć ogólny widok, który przyjmuje dowolny widok i jedenonClickListener
i stosuje się odpowiednio. Lub przekaż jeden poziom do koordynatora, aby kilka fragmentów / działań miało tę samą listę z różnymi zachowaniami klikania. Ponownie, cała elastyczność jest po twojej stronie.Jest to naprawdę potrzebny komponent i dość blisko naszych dotychczasowych wewnętrznych wdrożeń i ulepszeń
ListView
. Dobrze, że Google w końcu to potwierdza.źródło
onBindViewHolder(holder, position)
, w którym informacje powinny być wypełnione.getPosition()
jest przestarzałe "Ta metoda jest przestarzała, ponieważ jej znaczenie jest niejednoznaczne ze względu na asynchroniczną * obsługę aktualizacji adaptera. Użyj {@link #getLayoutPosition ()} lub * {@link #getAdapterPosition ()} w zależności od przypadku użycia. „viewHolder.getPosition();
Dlaczego RecyclerView nie ma
onItemClickListener
RecyclerView
To zestaw narzędzi, w przeciwieństwie do staregoListView
ma mniej build funkcji i większą elastyczność. ToonItemClickListener
nie jedyna funkcja usuwana z ListView. Ale ma wielu słuchaczy i metodę, aby rozszerzyć go na twoje upodobania, jest o wiele potężniejszy w odpowiednich rękach;).Moim zdaniem najbardziej złożoną funkcją usuniętą
RecyclerView
jest Szybki przewijanie . Większość innych funkcji można łatwo ponownie wdrożyć.Jeśli chcesz wiedzieć, jakie inne fajne funkcje zostały
RecyclerView
dodane, przeczytaj tę odpowiedź na inne pytanie.Wydajna pamięć - rozwiązanie rozwijane dla onItemClickListener
To rozwiązanie zostało zaproponowane przez Hugo Vissera , Androida GDE, zaraz po
RecyclerView
wydaniu. Udostępnił ci klasę bez licencji, abyś mógł po prostu wpisać swój kod i go użyć.Pokazuje niektóre z wszechstronności wprowadzonej
RecyclerView
dzięki wykorzystaniuRecyclerView.OnChildAttachStateChangeListener
.Edytuj 2019 : moja wersja kotlin, java one od Hugo Visser, trzymana poniżej
Kotlin / Java
Utwórz plik
values/ids.xml
i umieść w nim:następnie dodaj poniższy kod do swojego źródła
Kotlin
Stosowanie:
(obsługuje także długie klikanie elementów i zobacz poniżej inną funkcję, którą dodałem).
implementacja (moja adaptacja do kodu Java Hugo Visser):
(patrz poniżej, musisz również dodać plik XML)
Funkcja bonusowa wersji Kotlin
Czasami nie chcesz, aby wszystkie elementy RecyclerView były klikalne.
Aby sobie z tym poradzić, wprowadziłem
ItemClickSupportViewHolder
interfejs, którego możesz używaćViewHolder
do kontrolowania, który element można kliknąć.Przykład:
Jawa
Stosowanie:
(obsługuje również długie kliknięcie przedmiotu)
Realizacja (komentarze dodane przeze mnie):
Jak to działa (dlaczego jest wydajne)
Ta klasa działa poprzez dołączenie a
RecyclerView.OnChildAttachStateChangeListener
doRecyclerView
. Ten słuchacz jest powiadamiany za każdym razem, gdy dziecko jest przyłączane lub odłączane odRecyclerView
. Kod używa tego, aby dołączyć do widoku detektora stuknięcie / długie kliknięcie. Ten słuchacz pytaRecyclerView
o to,RecyclerView.ViewHolder
która zawiera pozycję.Jest to bardziej wydajne niż inne rozwiązania, ponieważ pozwala uniknąć tworzenia wielu detektorów dla każdego widoku i ciągle je niszczy i tworzy podczas
RecyclerView
przewijania.Możesz również dostosować kod, aby zwrócić samego posiadacza, jeśli potrzebujesz więcej.
Uwaga końcowa
Należy pamiętać, że ZUPEŁNIE dobrze jest obsługiwać go w adapterze, ustawiając w każdym widoku listy odbiornik kliknięć, podobnie jak inne proponowane odpowiedzi.
To po prostu nie jest najbardziej wydajna czynność (tworzysz nowego nasłuchującego za każdym razem, gdy ponownie użyjesz widoku), ale działa, a w większości przypadków nie stanowi to problemu.
Jest to również trochę sprzeczne z rozdzielaniem problemów, ponieważ delegowanie zdarzeń kliknięcia nie jest tak naprawdę zadaniem adaptera.
źródło
addTo()
iremoveFrom()
. Instancja jest powiązana z tagiem recyclinglerview i umiera wraz z nim lub po ręcznym usunięciu.onCreateViewHolder
i to wszystko.RecyclerView
.Podoba mi się w ten sposób i używam go
Wewnątrz
Położyć
I stwórz tę klasę gdziekolwiek chcesz
Przeczytałem wcześniej, że istnieje lepszy sposób, ale podoba mi się, że ten sposób jest łatwy i nieskomplikowany.
źródło
MyOnClickListener
w instancję zmienną, ponieważ będziesz tworzył nową instancję za każdym razem za pomocą słowanew
kluczowegorecyclerView
do MyOnClickListener?Android Recyclerview With
onItemClickListener
, Dlaczego nie możemy spróbować, to działa jakListView
tylko.Źródło: Link
I ustaw to na RecyclerView:
źródło
SwipeRefreshLayout
Dzięki @marmor zaktualizowałem swoją odpowiedź.
Myślę, że to dobre rozwiązanie do obsługi onClick () w konstruktorze klasy ViewHolder i przekazania go do klasy nadrzędnej poprzez interfejs OnItemClickListener .
MyAdapter.java
Zastosowanie adaptera w innych klasach:
MyFragment.java
źródło
onBindViewHolder()
Jest również nazywany wiele razy dla tego samego widoku podczas przewijania. Nawet utworzenie jednegoOnClickListener
nie pomoże, ponieważ będziesz resetowany zaOnClickListener
każdym razem, gdyonBindViewHolder()
zostanie wywołane. Zobacz zamiast tego @ MLProgrammer-CiM rozwiązanie.MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....
, możesz utworzyć jeden detektor dla wszystkich widoków.Faceci używają tego kodu w głównej działalności. Bardzo skuteczna metoda
Oto twoja klasa adaptera.
Następnie otrzymasz tę metodę zastępowania w swojej aktywności.
źródło
> Czym RecyclerView różni się od widoku listy?
Jedną różnicą jest to, że istnieje
LayoutManager
klasa z RecyclerView, dzięki której możesz zarządzać swoimiRecyclerView
podobnymi-Podobnie jak w przypadku przewijania w poziomie dla RecyclerView-
źródło
Kontynuując doskonałe rozwiązanie RxJava MLProgrammer-CIM
Zużyj / Obserwuj kliknięcia
RxJava 2. +
Zmodyfikuj oryginalny tl; dr jako:
PublishSubject#asObservable()
zostało usunięte. Zwróć tylko to,PublishSubject
co jestObservable
.źródło
Jak złożyć to wszystko przykład ...
Typy ViewHolder
źródło
O ile rozumiem odpowiedź MLProgrammer-CiM , po prostu można to zrobić:
źródło
Po przeczytaniu odpowiedzi @ MLProgrammer-CiM , oto mój kod:
źródło
getAdapterPosition
zwrócić-1
„brak pozycji”.Zrobiłem w ten sposób, to bardzo proste:
Wystarczy dodać 1 linię dla klikniętego RecyclerView pozycji :
Pełny kod dla klasy ViewHolder :
Mam nadzieję, że to ci pomoże.
źródło
getAdapterPosition
zamiastgetLayoutPosition
. Strzeż się-1
.Używam tej metody, aby uruchomić zamiar z RecyclerView:
źródło
onBindViewHolder(...)
nie jest wywoływany ponotify()
metodach. W niektórych sytuacjach możesz uzyskać niewłaściwą pozycję kliknięcia.RecyclerView nie ma,
onItemClickListener
ponieważ RecyclerView jest odpowiedzialny za recykling widoków (niespodzianka!), Więc jest odpowiedzialny za widok, który jest przetwarzany, aby obsłużyć otrzymywane zdarzenia kliknięcia.To w rzeczywistości znacznie ułatwia korzystanie, zwłaszcza jeśli masz przedmioty, które można kliknąć w wielu miejscach.
W każdym razie wykrycie kliknięcia elementu RecyclerView jest bardzo łatwe. Wszystko, co musisz zrobić, to zdefiniować interfejs (jeśli nie używasz Kotlina, w takim przypadku po prostu przekazujesz lambda):
Ten sam kod w Kotlinie:
Rzecz, której NIE musisz robić:
1.) nie musisz ręcznie przechwytywać zdarzeń dotykowych
2.) nie musisz zadzierać ze słuchaczami dołączającymi zmiany stanu dziecka
3.) nie potrzebujesz PublishSubject / PublishRelay z RxJava
Wystarczy użyć detektora kliknięć.
źródło
Zobacz moje podejście do tego:
Najpierw zadeklaruj taki interfejs:
Następnie utwórz adapter:
A teraz zobaczmy, jak zintegrować to z fragmentu:
źródło
Jeśli chcesz dodać onClick () do podrzędnego widoku elementów, na przykład przycisku w elemencie, odkryłem, że możesz to łatwo zrobić w onCreateViewHolder () własnego RecyclerView.Adapter tak:
nie wiem, czy to dobry sposób, ale działa dobrze. Jeśli ktoś ma lepszy pomysł, bardzo miło mi powiedzieć i poprawić moją odpowiedź! :)
źródło
Oto sposób na dość łatwą implementację, jeśli masz listę POJO i chcesz pobrać jedno po kliknięciu spoza adaptera.
W adapterze utwórz detektor dla zdarzeń kliknięcia i metodę ustawienia:
}
W swoim ViewHolder zaimplementuj onClickListener i utwórz członka klasy, aby tymczasowo przechowywać POJO, który widok przedstawia w ten sposób (jest to przykład, lepiej byłoby utworzyć seter):
Z powrotem w adapterze ustaw bieżące POJO, gdy ViewHolder jest powiązany (lub zerowy, jeśli bieżący widok go nie ma):
To wszystko, teraz możesz użyć tego w następujący sposób ze swojego fragmentu / aktywności:
źródło
mMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
tak, możesz
źródło
Tutaj możesz obsługiwać wiele kliknięć, patrz poniższy kod i jest to bardzo wydajne
źródło
itemView
.Zmodyfikowałem mój komentarz ...
źródło
getAdapterPosition()
.Sprawdź ten, w którym zaimplementowałem wszystkie rzeczy we właściwy sposób
Klasa RecyclerViewHolder
Adapter
Interfejs
źródło
To działało dla mnie:
źródło
setOnClickListener(holder)
wonBindViewHolder()
. Ponieważ onBindViewHolder (...) nie jest wywoływany po metod powiadomień (). W niektórych sytuacjach możesz uzyskać niewłaściwą pozycję kliknięcia.onCreateViewHolder
.Uzyskaj dostęp do
mainView
irowLayout(cell)
dla siebieRecyclerView
iOnBindViewHolder
napisz ten kod:źródło
Zamiast implementować interfejs View.OnClickListener wewnątrz uchwytu widoku lub tworzyć interfejs i implementować interfejs w twojej działalności. Użyłem tego kodu do prostej implementacji OnClickListener.
źródło
Uchwyt do widoku wnętrza
Inside OnBindViewHolder ()
Daj mi znać, czy masz jakieś pytania dotyczące tego rozwiązania?
źródło
onCreateViewHolder
celu uzyskania lepszej wydajności, ale potem musisz uzyskać pozycję za pomocągetAdapterPosition()
(i strzec się-1
)źródło
użyj PlaceHolderView
źródło
Napisałem bibliotekę do obsługi zdarzenia kliknięcia elementu wyświetlającego recykling androida. Cały samouczek można znaleźć w https://github.com/ChathuraHettiarachchi/RecycleClick
lub do obsługi długiego naciśnięcia elementu, którego możesz użyć
źródło
animacja recyclinglerview nie została przetestowana, druga jest normalna. Myślę, że został zoptymalizowany do maksimum. Interfejs ma inne zastosowania, które możesz tymczasowo zignorować.
To jest interfejs
To jest klasa abstrakcyjna
Potrzebujesz tylko tego
źródło
Przykład z getTag () / setTag () i detektorem pojedynczego kliknięcia dla całego adaptera:
TAG_POSITION zdefiniowany w pliku tags.xml jako
źródło