Od https://developer.android.com/preview/material/ui-widgets.html
Podczas tworzenia RecyclerView.Adapter
musimy określić, ViewHolder
że będzie się łączyć z adapterem.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private String[] mDataset;
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(TextView v) {
super(v);
mTextView = v;
}
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);
//findViewById...
ViewHolder vh = new ViewHolder(v);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTextView.setText(mDataset[position]);
}
@Override
public int getItemCount() {
return mDataset.length;
}
}
Czy można tworzyć RecyclerView
z wieloma typami widoków?
Odpowiedzi:
Tak, to możliwe. Wystarczy zaimplementować getItemViewType () i zająć się
viewType
parametrem wonCreateViewHolder()
.Więc robisz coś takiego:
źródło
optionMenuItem
. Jak to możliwe? @AntonSavinJeśli układy typów widoków są tylko kilka, a logika wiązania jest prosta, zastosuj rozwiązanie Antona.
Ale kod będzie bałagan, jeśli musisz zarządzać złożonymi układami i logiką wiązania.
Uważam, że poniższe rozwiązanie przyda się komuś, kto musi obsługiwać złożone typy widoków.
Podstawowa klasa DataBinder
Funkcje potrzebne do zdefiniowania w tej klasie są prawie takie same jak klasa adaptera podczas tworzenia typu pojedynczego widoku.
Dla każdego typu widoku utwórz klasę, rozszerzając ten DataBinder.
Przykładowa klasa DataBinder
Aby zarządzać klasami DataBinder, utwórz klasę adaptera.
Podstawowa klasa DataBindAdapter
Utwórz klasę, rozszerzając tę klasę podstawową, a następnie utwórz instancję klas DataBinder i zastąp metody abstrakcyjne
getItemCount
Zwraca całkowitą liczbę elementów DataBinders
getItemViewType
Zdefiniuj logikę mapowania między pozycją adaptera a typem widoku.
getDataBinder
Zwraca instancję DataBinder na podstawie typu widoku
getPosition
Definiuje konwersję logiki do pozycji adaptera z pozycji w określonym DataBinder
getBinderPosition
Zdefiniuj konwersję logiki do pozycji w DataBinder z pozycji adaptera
Mam nadzieję, że to rozwiązanie będzie pomocne.
Zostawiłem bardziej szczegółowe rozwiązanie i próbki w GitHub, więc w razie potrzeby skorzystaj z poniższego linku.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter
źródło
public class DataBinder<T extends RecyclerView.ViewHolder>
czy ktoś może mi powiedzieć, jak<T someClass>
się nazywa, więc mogę google, jeśli dostanę termin. Także kiedy mówię,abstract public class DataBinder<T extends RecyclerView.ViewHolder>
czy to oznacza, że ta klasa jest typuViewHolder
, więc w rezultacie każda klasa, która ją rozszerza, będzie typu,viewHolder
czy to jest pomysł?Poniższy kod nie jest pseudokodem, przetestowałem go i zadziałał dla mnie.
Chciałem utworzyć widok nagłówka w moim widoku recyklera, a następnie wyświetlić listę zdjęć poniżej nagłówka, którą użytkownik może kliknąć.
Użyłem kilku przełączników w moim kodzie, nie wiem, czy jest to najbardziej efektywny sposób, więc nie krępuj się i komentuj:
źródło
Tak to mozliwe.
Napisz ogólny uchwyt widoku:
następnie utwórz uchwyty widoków i zmuś je, aby rozszerzyły GenericViewHolder. Na przykład ten:
wtedy klasa RecyclerView.Adapter będzie wyglądać następująco:
źródło
Utwórz inny ViewHolder dla innego układu
RecyclerView może mieć dowolną liczbę wyświetlaczy, ale dla lepszej czytelności pozwala zobaczyć, jak utworzyć jeden z dwoma ViewHolderami.
public int getItemViewType(int position)
onCreateViewHolder()
metodzieonBindViewHolder()
metodzieOto mały fragment kodu
getItemViewType (pozycja wewnętrzna) jest kluczem
Moim zdaniem punktem wyjścia do stworzenia tego rodzaju recykleraView jest znajomość tej metody. Ponieważ ta metoda jest opcjonalna do zastąpienia, dlatego nie jest domyślnie widoczna w klasie RecylerView, co z kolei sprawia, że wielu programistów (w tym ja) zastanawia się, od czego zacząć. Gdy dowiesz się, że ta metoda istnieje, utworzenie takiego RecyclerView byłoby bułką z masłem.
Ważne linki:
Sprawdź projekt, w którym to zrealizowałem
źródło
Tak to mozliwe. W twoim układzie getItemViewType układ taki jak ten ....
dla odniesienia: https://www.journaldev.com/12372/android-recyclerview-example
źródło
zgodnie z rozwiązaniem Antona, wymyśl to,
ViewHolder
które utrzymuje / obsługuje / deleguje różne typy układów. Ale nie jestem pewien, czy zastąpienie nowego układu będzie działać, gdy widok recyklinguViewHolder
nie jest rodzajem gromadzenia danych.Zasadniczo
onCreateViewHolder(ViewGroup parent, int viewType)
jest wywoływany tylko wtedy, gdy potrzebny jest nowy układ widoku;getItemViewType(int position)
zostanie wezwany doviewType
;onBindViewHolder(ViewHolder holder, int position)
jest zawsze wywoływany podczas recyklingu widoku (wprowadzane są nowe dane i próbuje się z nimi wyświetlićViewHolder
).Kiedy więc
onBindViewHolder
się nazywa, musi umieścić odpowiedni układ widoku i zaktualizowaćViewHolder
.Czy sposób zastąpienia układu widoku jest poprawny
ViewHolder
, czy może jakiś problem? Doceń każdy komentarz!Edycja: ViewHolder ma element mItemViewType do przechowywania widoku
Edycja: wygląda jak w onBindViewHolder (uchwyt ViewHolder, pozycja int) przekazany ViewHolder został odebrany (lub utworzony) przez sprawdzenie getItemViewType (pozycja int), aby upewnić się, że jest on zgodny, więc nie trzeba się martwić, że ViewHolder typ nie pasuje do typu danych [pozycja]. Czy ktoś wie więcej o tym, jak pobierane jest ViewHolder w onBindViewHolder ()?
Edycja: Wygląda na to, że Kosz
ViewHolder
jest wybierany według typu, więc nie ma tam wojownika.Edycja: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ odpowiada na to pytanie.
Otrzymuje recykling
ViewHolder
jak:lub utwórz nowy, jeśli nie znajdziesz recyklingu
ViewHolder
odpowiedniego typu.źródło
Mam lepsze rozwiązanie, które pozwala tworzyć wiele typów widoków w sposób deklaratywny i bezpieczny dla typu. Jest napisany w Kotlinie, który jest naprawdę fajny.
Proste uchwyty widoków dla wszystkich wymaganych typów widoków
Istnieje abstrakcja elementu danych adaptera. Zauważ, że typ widoku jest reprezentowany przez hashCode określonej klasy posiadacza widoku (KClass w Kotlin)
Tylko
bindViewHolder
musi być nadpisane w konkretnych klasach poz adapter (typ bezpieczny sposób)Lista takich
AdapterItemMedium
obiektów jest źródłem danych dla adaptera, który faktycznie akceptuje,List<AdapterItem>
patrz poniżej.Ważną częścią tego rozwiązania jest fabryka uchwytów widoków, która zapewni nowe wystąpienia określonego ViewHolder
Prosta klasa adaptera wygląda następująco
Tylko 3 kroki, aby utworzyć nowy typ widoku:
ViewHolderProvider
Oto przykład tej koncepcji: android-szuflada-szablon To idzie jeszcze dalej - zobacz typ, który działa jak element spinnera, elementy adaptera do wyboru.
źródło
To jest bardzo proste i proste.
Wystarczy zastąpić metodę getItemViewType () w adapterze. Na podstawie danych zwracane są różne wartości itemViewType. np. rozważ obiekt typu Person z członkiem isMale, jeśli isMale ma wartość true, zwróć 1, a isMale ma wartość false, zwróć 2 w metodzie getItemViewType () .
Teraz przychodzi do createViewHolder (nadrzędny ViewGroup, int viewType) , na podstawie różnych viewType, które można napompować inny plik układu. jak poniżej
w onBindViewHolder (uchwyt VH, pozycja int) sprawdź, gdzie uchwyt jest instancją
AdapterFemaleViewHolder
lubAdapterMaleViewHolder
przezinstanceof
i odpowiednio przypisz wartości.ViewHolder Może tak być
źródło
Chociaż wybrana odpowiedź jest poprawna, chcę ją bardziej szczegółowo rozwinąć. Znalazłem tutaj przydatny adapter niestandardowy dla wielu typów widoków w RecyclerView . Jego wersja Kotlin jest tutaj .
Następuje niestandardowy adapter
źródło
Polecam tę bibliotekę od Hannesa Dorfmanna. Zawiera całą logikę związaną z danym typem widoku w oddzielnym obiekcie o nazwie „AdapterDelegate”. https://github.com/sockeqwe/AdapterDelegates
źródło
Właściwie chciałbym poprawić odpowiedź Antona .
Ponieważ
getItemViewType(int position)
zwraca wartość całkowitą, możesz zwrócić identyfikator zasobu układu, który musisz zawęzić. W ten sposób zaoszczędzisz trochę logiki wonCreateViewHolder(ViewGroup parent, int viewType)
metodzie.Ponadto nie sugerowałbym wykonywania intensywnych obliczeń,
getItemCount()
ponieważ ta konkretna funkcja jest wywoływana co najmniej 5 razy podczas renderowania listy, a także podczas renderowania każdego elementu poza elementy widoczne. Niestety, ponieważnotifyDatasetChanged()
metoda jest ostateczna, tak naprawdę nie można jej przesłonić, ale można ją wywołać z innej funkcji w adapterze.źródło
Note: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.
Więc lepiej napisać nieco więcej kodu i nie używać hacków.Możesz użyć biblioteki: https://github.com/vivchar/RendererRecyclerViewAdapter
`
Dla każdego elementu należy zaimplementować ViewRenderer, ViewHolder, SomeModel:
ViewHolder - jest to prosty uchwyt widoku widoku recyklera.
SomeModel - to Twój model z
ItemModel
interfejsemAby uzyskać więcej informacji, możesz przejrzeć dokumentację.
źródło
Implementacja typów widoków staje się łatwiejsza dzięki kotlin, oto przykład z tą lekką biblioteką https://github.com/Link184/KidAdapter
źródło
Można sobie multipleViewTypes
RecyclerAdapter
dokonującgetItemViewType()
zwrotu oczekiwanejviewType
wartości dla tej pozycjiPrzygotowałem
MultipleViewTypeAdapter
do skonstruowania listy MCQ do egzaminów, które mogą zadać pytanie, które może zawierać 2 lub więcej poprawnych odpowiedzi (opcje pola wyboru) i pytania z jedną odpowiedzią (opcje przycisku radiowego).W tym celu otrzymałem typ pytania z odpowiedzi API i użyłem go do podjęcia decyzji, który widok muszę pokazać dla tego pytania.
Można uniknąć wielokrotnego wypełniania danych viewHolder opartych na warunkach,
onBindViewHolder()
przypisując te same identyfikatory do podobnych widoków w różnych viewHolderach, które różnią się położeniem.źródło
Jeśli chcesz go używać w połączeniu z Android Data Binding, zajrzyj do https://github.com/evant/binding-collection-adapter - jest to zdecydowanie najlepsze rozwiązanie dla wielu typów widoków
RecyclerView
, jakie widziałem.możesz go użyć jak
a następnie w układzie, w którym znajduje się lista
elementy listy muszą implementować
BaseListItem
interfejs, który wygląda następującoa widok elementu powinien wyglądać mniej więcej tak
Gdzie
YourListItem
implementujeBaseListItem
Mam nadzieję, że to komuś pomoże.
źródło
najpierw musisz utworzyć 2 xml układu. po tym w środku recyclinglerview adapter TYPE_CALL i TYPE_EMAIL mają dwie wartości statyczne, odpowiednio 1 i 2 w klasie adaptera.
teraz Zdefiniuj dwie wartości statyczne na poziomie klasy Recycler, na przykład: private static int TYPE_CALL = 1; prywatny statyczny int TYPE_EMAIL = 2;
Teraz utwórz uchwyt widoku z wieloma takimi widokami:
Teraz koduj jak poniżej w metodach onCreateViewHolder i onBindViewHolder w adapterze recyclinglerview:
źródło