Personalmente non mi piace sottoclassare RecyclerView per questo, perché per me sembra che ci sia la responsabilità di GridLayoutManager di rilevare il conteggio degli intervalli. Quindi, dopo un po 'di ricerca sul codice sorgente Android per RecyclerView e GridLayoutManager, ho scritto la mia classe GridLayoutManager estesa che fa il lavoro:
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);
}
}
In realtà non ricordo perché ho scelto di impostare il conteggio dello span in onLayoutChildren, ho scritto questo corso qualche tempo fa. Ma il punto è che dobbiamo farlo dopo che la vista è stata misurata. così possiamo ottenere la sua altezza e larghezza.
EDIT 1: correzione dell'errore nel codice causato dall'impostazione errata del conteggio dell'intervallo. Grazie all'utente @Elyees Abouda per aver segnalato e suggerito la soluzione .
EDIT 2: alcuni piccoli refactoring e fix edge case con gestione manuale delle modifiche dell'orientamento. Grazie all'utente @tatarize per aver segnalato e suggerito la soluzione .
LayoutManager
compito di licenziare i bambini e nonRecyclerView
di