Osobiście nie lubię podklasy RecyclerView w tym celu, ponieważ wydaje mi się, że GridLayoutManager jest odpowiedzialny za wykrywanie liczby rozpiętości. Więc po pewnym kopaniu kodu źródłowego Androida dla RecyclerView i GridLayoutManager napisałem własną rozszerzoną klasę GridLayoutManager, która wykonuje zadanie:
public class GridAutofitLayoutManager extends GridLayoutManager
{
private int columnWidth;
private boolean isColumnWidthChanged = true;
private int lastWidth;
private int lastHeight;
public GridAutofitLayoutManager(@NonNull final Context context, final int columnWidth) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
public GridAutofitLayoutManager(
@NonNull final Context context,
final int columnWidth,
final int orientation,
final boolean reverseLayout) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1, orientation, reverseLayout);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
private int checkedColumnWidth(@NonNull final Context context, final int columnWidth) {
if (columnWidth <= 0) {
/* Set default columnWidth value (48dp here). It is better to move this constant
to static constant on top, but we need context to convert it to dp, so can't really
do so. */
columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
context.getResources().getDisplayMetrics());
}
return columnWidth;
}
public void setColumnWidth(final int newColumnWidth) {
if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
columnWidth = newColumnWidth;
isColumnWidthChanged = true;
}
}
@Override
public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
final int width = getWidth();
final int height = getHeight();
if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
final int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = width - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
final int spanCount = Math.max(1, totalSpace / columnWidth);
setSpanCount(spanCount);
isColumnWidthChanged = false;
}
lastWidth = width;
lastHeight = height;
super.onLayoutChildren(recycler, state);
}
}
Właściwie nie pamiętam, dlaczego zdecydowałem się ustawić licznik rozpiętości w onLayoutChildren, napisałem tę klasę jakiś czas temu. Ale chodzi o to, że musimy to zrobić po zmierzeniu widoku. abyśmy mogli uzyskać jego wysokość i szerokość.
EDYCJA 1: Napraw błąd w kodzie, który powodował nieprawidłowe ustawienie liczby rozpiętości. Dziękuję użytkownikowi @Elyees Abouda za zgłoszenie i zasugerowanie rozwiązania .
EDYCJA 2: Trochę małych refaktoryzacji i napraw krawędziowych z ręczną obsługą zmian orientacji. Dziękuję użytkownikowi @tatarize za zgłoszenie i zasugerowanie rozwiązania .
LayoutManager
jest zadanie, aby stworzyć dzieciom out i nieRecyclerView
'sgetWidth()
lubgetHeight()
wynosi 0 przed utworzeniem widoku, co spowoduje wyświetlenie nieprawidłowego spanCount (1, ponieważ totalSpace będzie wynosić <= 0). Dodałem w tym przypadku ignorowanie setSpanCount. (onLayoutChildren
zostanie ponownie wezwany później)Dokonałem tego za pomocą obserwatora drzewa widoków, aby uzyskać szerokość widoku recylcerview po wyrenderowaniu, a następnie uzyskać stałe wymiary widoku mojej karty z zasobów, a następnie ustawić liczbę rozpiętości po wykonaniu moich obliczeń. Ma to naprawdę zastosowanie tylko wtedy, gdy wyświetlane elementy mają stałą szerokość. Pomogło mi to automatycznie wypełnić siatkę niezależnie od rozmiaru ekranu lub orientacji.
źródło
ArrayIndexOutOfBoundsException
( na android.support.v7.widget.GridLayoutManager.layoutChunk (GridLayoutManager.java:361) ) podczas przewijania plikuRecyclerView
.removeGlobalOnLayoutListener()
jest nieaktualne w przypadku użycia interfejsu API 16. poziomuremoveOnGlobalLayoutListener()
. Dokumentacja .Cóż, to jest to, czego użyłem, dość podstawowe, ale wykonuje pracę za mnie. Ten kod w zasadzie pobiera szerokość ekranu w spadkach, a następnie dzieli przez 300 (lub jakąkolwiek szerokość, której używasz dla układu karty). Tak więc mniejsze telefony o szerokości zanurzenia 300-500 wyświetlają tylko jedną kolumnę, tablety 2-3 kolumny itd. Prosty, bezproblemowy i bez wad, o ile widzę.
źródło
Rozszerzyłem RecyclerView i zastąpiłem metodę onMeasure.
Ustawiam szerokość elementu (zmienną składową) tak wcześnie, jak to możliwe, z domyślną wartością 1. To również aktualizuje się po zmianie konfiguracji. Będzie miał teraz tyle wierszy, ile zmieści się w pionie, poziomie, telefonie / tablecie itp.
źródło
Publikuję to na wypadek, gdyby ktoś miał dziwną szerokość kolumny, jak w moim przypadku.
Nie mogę skomentować odpowiedzi @ s-marks z powodu mojej złej reputacji. Zastosowałem jego rozwiązanie , ale otrzymałem dziwną szerokość kolumny, więc zmodyfikowałem funkcję CheckColumnWidth w następujący sposób:
Konwersja podanej szerokości kolumny na DP rozwiązała problem.
źródło
Aby uwzględnić zmianę orientacji w odpowiedzi s- mark, dodałem kontrolę zmiany szerokości (szerokość z getWidth (), a nie szerokość kolumny).
źródło
Uznane rozwiązanie jest w porządku, ale traktuje przychodzące wartości jako piksele, co może spowodować błąd, jeśli zakodujesz na stałe wartości do testowania i przyjmujesz dp. Najłatwiejszym sposobem jest prawdopodobnie umieszczenie szerokości kolumny w wymiarze i odczytanie go podczas konfigurowania GridAutofitLayoutManager, który automatycznie przekonwertuje dp na prawidłową wartość piksela:
źródło
Kiedy tworzysz GridLayoutManager, musisz wiedzieć, ile kolumn będzie przy minimalnym rozmiarze imageView:
Następnie musisz zmienić rozmiar imageView w adapterze, jeśli masz miejsce w kolumnie. Możesz wysłać newImageViewSize, a następnie zainizilizować adapter z aktywności, w której obliczasz liczbę ekranów i kolumn:
Działa w obu orientacjach. W pionie mam 2 kolumny, aw poziomie - 4 kolumny. Wynik: https://i.stack.imgur.com/WHvyD.jpg
źródło
Podsumowując powyższe odpowiedzi tutaj
źródło
To jest klasa s.maks z drobną poprawką dotyczącą zmiany rozmiaru samego widoku recyklingu. Na przykład, gdy sam zajmujesz się zmianami orientacji (w manifeście
android:configChanges="orientation|screenSize|keyboardHidden"
) lub z innego powodu widok recyklingu może zmienić rozmiar bez zmiany mColumnWidth. Zmieniłem również wartość int, która ma być zasobem rozmiaru, i pozwoliłem konstruktorowi bez zasobu, a następnie setColumnWidth zrobić to sam.źródło
Ustaw spanCount na dużą liczbę (która jest maksymalną liczbą kolumn) i ustaw niestandardowy SpanSizeLookup na GridLayoutManager.
To trochę brzydkie, ale działa.
Myślę, że menedżer taki jak AutoSpanGridLayoutManager byłby najlepszym rozwiązaniem, ale nie znalazłem czegoś takiego.
EDYCJA: Wystąpił błąd, na niektórych urządzeniach dodaje puste miejsce po prawej stronie
źródło
getSpanSize
zwraca 3, będzie spacja, ponieważ nie wypełniasz zakresu.Oto odpowiednie części opakowania, którego używałem do automatycznego wykrywania liczby rozpiętości. Zainicjować go poprzez wywołanie
setGridLayoutManager
z R.layout.my_grid_item odniesienia, i domyśla się, ile osób zmieści się w każdym wierszu.źródło