Persönlich möchte ich RecyclerView dafür nicht unterordnen, da es für mich so scheint, als ob GridLayoutManager dafür verantwortlich ist, die Anzahl der Bereiche zu ermitteln. Nachdem ich einige Android-Quellcodes für RecyclerView und GridLayoutManager ausgegraben hatte, schrieb ich meinen eigenen klassenerweiterten GridLayoutManager, der die Aufgabe erledigt:
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);
}
}
Ich erinnere mich nicht wirklich, warum ich mich entschieden habe, die Anzahl der Spannen in onLayoutChildren festzulegen. Ich habe diese Klasse vor einiger Zeit geschrieben. Der Punkt ist jedoch, dass wir dies tun müssen, nachdem die Ansicht gemessen wurde. so können wir seine Höhe und Breite erhalten.
BEARBEITEN 1: Fehler im Code behoben, der dazu führte, dass die Anzahl der Bereiche falsch eingestellt wurde. Vielen Dank an Benutzer @Elyees Abouda für die Berichterstattung und den Lösungsvorschlag .
EDIT 2: Einige kleine Refactoring- und Fixkantengehäuse mit manueller Ausrichtung ändern die Handhabung. Vielen Dank an Benutzer @tatarize für die Berichterstellung und den Lösungsvorschlag .
LayoutManager
's Aufgabe , die Kinder und nicht zu legenRecyclerView
istgetWidth()
odergetHeight()
ist 0, bevor die Ansicht erstellt wird, wodurch ein falscher spanCount angezeigt wird (1, da totalSpace <= 0 ist). Was ich hinzugefügt habe, ist in diesem Fall setSpanCount zu ignorieren. (onLayoutChildren
wird später noch einmal genannt)Ich habe dies mit einem View Tree Observer erreicht, um die Breite der einmal gerenderten Recylcerview zu ermitteln, die festen Abmessungen meiner Kartenansicht aus den Ressourcen abzurufen und nach den Berechnungen die Anzahl der Bereiche festzulegen. Dies ist nur dann wirklich anwendbar, wenn die angezeigten Elemente eine feste Breite haben. Dadurch konnte ich das Raster unabhängig von Bildschirmgröße oder -ausrichtung automatisch ausfüllen.
quelle
ArrayIndexOutOfBoundsException
( unter android.support.v7.widget.GridLayoutManager.layoutChunk (GridLayoutManager.java:361) ) beim Scrollen derRecyclerView
.removeGlobalOnLayoutListener()
ist in API-Level 16 veraltet. Verwenden SieremoveOnGlobalLayoutListener()
stattdessen. Dokumentation .Nun, das ist es, was ich verwendet habe, ziemlich einfach, aber ich erledige die Arbeit für mich. Dieser Code ermittelt die Bildschirmbreite im Grunde genommen in Dips und dividiert sie durch 300 (oder die Breite, die Sie für das Layout Ihres Adapters verwenden). So zeigen kleinere Telefone mit einer Eintauchbreite von 300-500 nur eine Spalte, Tablets 2-3 Spalten usw. an. Einfach, unkompliziert und ohne Nachteile, soweit ich sehen kann.
quelle
Ich habe die RecyclerView erweitert und die onMeasure-Methode überschrieben.
Ich habe so früh wie möglich eine Elementbreite (Elementvariable) mit dem Standardwert 1 festgelegt. Dies wird auch bei geänderter Konfiguration aktualisiert. Dies hat jetzt so viele Zeilen, wie in Hochformat, Querformat, Telefon / Tablet usw. passen.
quelle
Ich poste dies nur für den Fall, dass jemand eine seltsame Spaltenbreite wie in meinem Fall bekommt.
Ich kann die Antwort von @ s-Marks aufgrund meines schlechten Rufs nicht kommentieren . Ich habe seine Lösungslösung angewendet , aber ich habe eine seltsame Spaltenbreite erhalten, daher habe ich die Funktion checkedColumnWidth wie folgt geändert :
Durch Konvertieren der angegebenen Spaltenbreite in DP wurde das Problem behoben.
quelle
Um die Orientierungsänderung in der Antwort von s- marken zu berücksichtigen, habe ich eine Überprüfung der Breitenänderung hinzugefügt (Breite von getWidth (), nicht Spaltenbreite).
quelle
Die Upvoted-Lösung ist in Ordnung, behandelt die eingehenden Werte jedoch als Pixel, was Sie auslösen kann, wenn Sie Werte zum Testen und zur Annahme von dp fest codieren. Am einfachsten ist es wahrscheinlich, die Spaltenbreite in eine Dimension zu setzen und sie bei der Konfiguration des GridAutofitLayoutManager zu lesen, der dp automatisch in den richtigen Pixelwert konvertiert:
quelle
Wenn Sie GridLayoutManager erstellen, müssen Sie wissen, wie viele Spalten bei minimaler Größe von imageView vorhanden sind:
Danach müssen Sie die Größe von imageView im Adapter ändern, wenn in der Spalte Platz vorhanden ist. Sie können newImageViewSize senden und dann den Adapter von der Aktivität dort deaktivieren, in der Sie die Anzahl der Bildschirme und Spalten berechnen:
Es funktioniert in beiden Ausrichtungen. In der Vertikalen habe ich 2 Spalten und in der Horizontalen - 4 Spalten. Das Ergebnis: https://i.stack.imgur.com/WHvyD.jpg
quelle
Ich schließe oben Antworten hier
quelle
Dies ist die Klasse von s.maks mit einer geringfügigen Korrektur für den Fall, dass sich die Größe der Recyclingansicht selbst ändert. Wenn Sie sich beispielsweise selbst (im Manifest
android:configChanges="orientation|screenSize|keyboardHidden"
) mit den Änderungen der Ausrichtung befassen , oder aus einem anderen Grund, kann sich die Größe der Recyclingansicht ändern, ohne dass sich die mColumnWidth ändert. Ich habe auch den int-Wert geändert, der als Ressource der Größe benötigt wird, und einem Konstruktor ohne Ressource erlaubt, dann setColumnWidth, dies selbst zu tun.quelle
Setzen Sie spanCount auf eine große Zahl (dies ist die maximale Anzahl von Spalten) und setzen Sie ein benutzerdefiniertes SpanSizeLookup auf den GridLayoutManager.
Es ist ein bisschen hässlich, aber es funktioniert.
Ich denke, ein Manager wie AutoSpanGridLayoutManager wäre die beste Lösung, aber ich habe so etwas nicht gefunden.
BEARBEITEN: Es gibt einen Fehler. Auf einigen Geräten wird rechts ein Leerzeichen hinzugefügt
quelle
getSpanSize
3 zurückgibt, wird ein Leerzeichen angezeigt, da Sie den Bereich nicht ausfüllen.Hier sind die relevanten Teile eines Wrappers, mit denen ich die Anzahl der Bereiche automatisch erkannt habe. Sie initialisieren es, indem Sie
setGridLayoutManager
mit einer R.layout.my_grid_item- Referenz aufrufen , und es wird ermittelt, wie viele davon in jede Zeile passen.quelle