Spędziłem chwilę, próbując znaleźć sposób na dodanie nagłówka do a RecyclerView
, ale bezskutecznie.
Oto, co osiągnąłem do tej pory:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
layouManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layouManager);
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
headerPlaceHolder = inflater.inflate(R.layout.view_header_holder_medium, null, false);
layouManager.addView(headerPlaceHolder, 0);
...
}
LayoutManager
Wydaje się być przedmiotem obchodzenia się rozmieszczenie RecyclerView
elementów. Ponieważ nie mogłem znaleźć żadnej addHeaderView(View view)
metody, zdecydowałem się skorzystać z metody LayoutManager
'' addView(View view, int position)
i dodać mój widok nagłówka na pierwszej pozycji, aby działał jako nagłówek.
Aa i tutaj sprawy stają się brzydsze:
java.lang.NullPointerException: Attempt to read from field 'android.support.v7.widget.RecyclerView$ViewHolder android.support.v7.widget.RecyclerView$LayoutParams.mViewHolder' on a null object reference
at android.support.v7.widget.RecyclerView.getChildViewHolderInt(RecyclerView.java:2497)
at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:4807)
at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4803)
at com.mathieumaree.showz.fragments.CategoryFragment.setRecyclerView(CategoryFragment.java:231)
at com.mathieumaree.showz.fragments.CategoryFragment.access$200(CategoryFragment.java:47)
at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:201)
at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:196)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:41)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Po kilku NullPointerExceptions
próbach wywołania addView(View view)
w różnych momentach tworzenia działania (próbowałem również dodać widok, gdy wszystko jest skonfigurowane, nawet dane adaptera), zdałem sobie sprawę, że nie mam pojęcia, czy jest to właściwy sposób (i to nie wygląda).
PS: Również rozwiązanie, które poradziłoby sobie z tym GridLayoutManager
oprócz tego, LinearLayoutManager
byłoby naprawdę mile widziane!
źródło
Odpowiedzi:
Musiałem dodać stopkę do mojego
RecyclerView
i tutaj udostępniam mój fragment kodu, ponieważ pomyślałem, że może być przydatny. Sprawdź komentarze w kodzie, aby lepiej zrozumieć ogólny przepływ.Powyższy fragment kodu dodaje stopkę do pliku
RecyclerView
. Możesz sprawdzić to repozytorium GitHub, aby sprawdzić implementację dodawania nagłówka i stopki.źródło
RecyclerView
dynamicznie wypełniać swój plik . Możesz mieć kontrolę nad każdym z elementów swojego plikuRecyclerView
. Proszę spojrzeć na sekcję kodu dla działającego projektu. Mam nadzieję, że to pomoże. github.com/comeondude/dynamic-recyclerview/wikiint getItemViewType (int position)
- Zwróć typ widoku elementu na miejscu w celu ponownego wyświetlenia. Domyślna implementacja tej metody zwraca 0, przy założeniu jednego typu widoku dla adaptera. W przeciwieństwie doListView
adapterów typy nie muszą być ciągłe. Rozważ użycie zasobów id, aby jednoznacznie identyfikować typy widoków pozycji. - Z dokumentacji. developer.android.com/reference/android/support/v7/widget/…Bardzo proste do rozwiązania !!
Nie podoba mi się pomysł posiadania logiki wewnątrz adaptera jako innego typu widoku, ponieważ za każdym razem sprawdza on typ widoku przed zwróceniem widoku. Poniższe rozwiązanie pozwala uniknąć dodatkowych kontroli.
Po prostu dodaj widok nagłówka LinearLayout (pionowy) + recykling + widok stopki wewnątrz android.support.v4.widget.NestedScrollView .
Sprawdź to:
Dodaj tę linię kodu, aby płynnie przewijać
Spowoduje to utratę wydajności kampera, a kamper będzie próbował rozłożyć wszystkich posiadaczy widoku, niezależnie
layout_height
od kamperaZalecane w przypadku małych list rozmiarów, takich jak szuflada Nav lub ustawienia itp.
źródło
RecyclerView
niesie ze sobą - tracisz rzeczywisty recykling i optymalizację, jaką przynosi.Miałem ten sam problem na Lollipopie i stworzyłem dwa podejścia do owijania
Recyclerview
adaptera. Jeden jest dość łatwy w użyciu, ale nie jestem pewien, jak będzie się zachowywał przy zmieniającym się zestawie danych. Ponieważ opakowuje on twój adapter i musisz upewnić się, że wywołujesz metody takie jaknotifyDataSetChanged
w odpowiednim obiekcie adaptera.Drugi nie powinien mieć takich problemów. Po prostu pozwól zwykłemu adapterowi rozszerzyć klasę, zaimplementuj metody abstrakcyjne i powinieneś być gotowy. A oto one:
istoty
new HeaderRecyclerViewAdapterV1(new RegularAdapter());
RegularAdapter extends HeaderRecyclerViewAdapterV2
HeaderRecyclerViewAdapterV1
HeaderRecyclerViewAdapterV2
Mile widziane opinie i widelce. Będę używać
HeaderRecyclerViewAdapterV2
samodzielnie i ewoluować, testować i publikować zmiany w przyszłości.EDYCJA : @OvidiuLatcu Tak, miałem pewne problemy. Właściwie przestałem niejawnie kompensować Header
position - (useHeader() ? 1 : 0)
i zamiast tego utworzyłemint offsetPosition(int position)
dla niego metodę publiczną . Ponieważ jeśli ustawiszOnItemTouchListener
na Recyclerview, możesz przechwycić dotyk, uzyskać współrzędne x, y dotyku, znaleźć odpowiedni widok dziecka, a następnie zadzwonić,recyclerView.getChildPosition(...)
a zawsze otrzymasz nieprzesuniętą pozycję w adapterze! To jest niedociągnięcie w kodzie RecyclerView, nie widzę łatwej metody na pokonanie tego. Dlatego teraz ustawiam pozycje wyraźnie, gdy jest to konieczne, za pomocą własnego kodu.źródło
Nie próbowałem tego, ale po prostu dodam 1 (lub 2, jeśli chcesz, aby zarówno nagłówek, jak i stopka) do liczby całkowitej zwróconej przez getItemCount w twoim adapterze. Następnie możesz nadpisać
getItemViewType
w adapterze, aby zwrócić inną liczbę całkowitą, gdyi==0
: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)createViewHolder
jest następnie przekazywana liczba całkowita, z której zwróconogetItemViewType
, co pozwala na utworzenie lub skonfigurowanie uchwytu widoku w inny sposób dla widoku nagłówka: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html# createViewHolder (android.view.ViewGroup , int)Nie zapomnij odjąć jednego z przekazanej liczby całkowitej pozycji
bindViewHolder
.źródło
getItemViewType(int position) { return position == 0 ? 0 : 1; }
(RecyclerView
nie magetViewTypeCount()
metody). Z drugiej strony zgadzam się z @IZI_Shadow_IZI, naprawdę mam wrażenie, że LayoutManager powinien zajmować się tego rodzaju rzeczami. Masz inny pomysł?Możesz użyć tej biblioteki GitHub, która umożliwia dodanie nagłówka i / lub stopki w RecyclerView w najprostszy możliwy sposób.
Musisz dodać bibliotekę HFRecyclerView do swojego projektu lub możesz ją również pobrać z Gradle:
To jest wynik na obrazie:
EDYTOWAĆ:
Jeśli chcesz tylko dodać margines na górze i / lub na dole za pomocą tej biblioteki: SimpleItemDecoration :
źródło
Skończyło się na zaimplementowaniu własnego adaptera, aby opakować dowolny inny adapter i zapewnić metody dodawania widoków nagłówka i stopki.
Utworzono streszczenie tutaj: HeaderViewRecyclerAdapter.java
Główną funkcją, której potrzebowałem, był podobny interfejs do ListView, więc chciałem mieć możliwość zawyżania widoków w moim fragmencie i dodawania ich do pliku
RecyclerView
inonCreateView
. Odbywa się to poprzez utworzenieHeaderViewRecyclerAdapter
przekazującego adapter, który ma zostać opakowany, oraz wywołanieaddHeaderView
iaddFooterView
przekazanie zawyżonych widoków. Następnie ustawHeaderViewRecyclerAdapter
instancję jako adapter wRecyclerView
.Dodatkowym wymaganiem było to, że musiałem mieć możliwość łatwej wymiany adapterów, zachowując nagłówki i stopki. Nie chciałem mieć wielu adapterów z wieloma wystąpieniami tych nagłówków i stopek. Możesz więc wywołać
setAdapter
zmianę opakowanego adaptera, pozostawiając nienaruszone nagłówki i stopki, zRecyclerView
powiadomieniem o zmianie.źródło
mój sposób na proste, głupie ... marnuje trochę zasobów, wiem, ale nie obchodzi mnie to, ponieważ mój kod jest prosty, więc ... 1) dodaj stopkę z widocznością GONE do twojego item_layout
2), a następnie ustaw go jako widoczny na ostatniej pozycji
zrób odwrotnie dla nagłówka
źródło
W oparciu o rozwiązanie @ seb stworzyłem podklasę RecyclerView.Adapter, która obsługuje dowolną liczbę nagłówków i stopek.
https://gist.github.com/mheras/0908873267def75dc746
Chociaż wydaje się to rozwiązaniem, uważam również, że tym rozwiązaniem powinien zarządzać LayoutManager. Niestety, potrzebuję go teraz i nie mam czasu na implementację StaggeredGridLayoutManager od podstaw (ani nawet rozszerzanie z niego).
Wciąż to testuję, ale możesz to wypróbować, jeśli chcesz. Daj mi znać, jeśli znajdziesz z tym jakieś problemy.
źródło
Możesz użyć viewtype, aby rozwiązać ten problem, oto moje demo: https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView
możesz zdefiniować tryb wyświetlania widoku recyklera:
publiczne statyczne końcowe int MODE_DATA = 0, MODE_LOADING = 1, MODE_ERROR = 2, MODE_EMPTY = 3, MODE_HEADER_VIEW = 4, MODE_FOOTER_VIEW = 5;
2. zastąpić mothod getItemViewType
3. zastąpić metodę getItemCount
4. zastąpić metodę onCreateViewHolder. utwórz uchwyt widoku według viewType
5. Zastąp metodę onBindViewHolder. powiązać dane według typu widoku
źródło
Możesz użyć biblioteki SectionedRecyclerViewAdapter, aby pogrupować elementy w sekcje i dodać nagłówek do każdej sekcji, jak na poniższym obrazku:
Najpierw utwórz klasę sekcji:
Następnie skonfiguruj RecyclerView ze swoimi sekcjami i zmień SpanSize nagłówków za pomocą GridLayoutManager:
źródło
Wiem, że się spóźniam, ale dopiero niedawno udało mi się zaimplementować taki „addHeader” do Adaptora. W moim FlexibleAdapter projektu można zadzwonić
setHeader
na Sectionable elementu, a następnie zadzwonićshowAllHeaders
. Jeśli potrzebujesz tylko 1 nagłówka, pierwszy element powinien mieć nagłówek. Jeśli usuniesz ten element, nagłówek zostanie automatycznie połączony z następnym.Niestety stopki nie są (jeszcze) objęte.
FlexibleAdapter pozwala na znacznie więcej niż tworzenie nagłówków / sekcji. Naprawdę powinieneś rzucić okiem: https://github.com/davideas/F flexibleAdapter .
źródło
Po prostu dodałbym alternatywę do wszystkich tych implementacji HeaderRecyclerViewAdapter. CompoundAdapter:
https://github.com/negusoft/CompoundAdapter-android
Jest to bardziej elastyczne podejście, ponieważ można utworzyć grupę adapterów z adapterów. W przykładzie z nagłówkiem użyj adaptera w takiej postaci, w jakiej jest, wraz z adapterem zawierającym jeden element dla nagłówka:
Jest dość prosty i czytelny. Na tej samej zasadzie można łatwo zaimplementować bardziej złożony adapter.
źródło
recyclerview:1.2.0
wprowadza klasę ConcatAdapter, która łączy wiele adapterów w jeden. Pozwala więc na tworzenie oddzielnych adapterów nagłówka / stopki i ponowne wykorzystanie ich na wielu listach.Przeczytaj artykuł z ogłoszeniem . Zawiera przykład, jak wyświetlić postęp ładowania w nagłówku i stopce za pomocą
ConcatAdapter
.Na razie kiedy piszę tę odpowiedź wersja
1.2.0
biblioteki jest w fazie alfa, więc api może się zmienić. Możesz sprawdzić status tutaj .źródło