Personnellement, je n'aime pas sous-classer RecyclerView pour cela, car pour moi, il semble que GridLayoutManager ait la responsabilité de détecter le nombre de plages. Donc, après quelques recherches de code source Android pour RecyclerView et GridLayoutManager, j'ai écrit ma propre classe étendue GridLayoutManager qui fait le travail:
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);
}
}
Je ne me souviens pas vraiment pourquoi j'ai choisi de définir le nombre de span dans onLayoutChildren, j'ai écrit ce cours il y a quelque temps. Mais le fait est que nous devons le faire après avoir mesuré la vue. afin que nous puissions obtenir sa hauteur et sa largeur.
EDIT 1: Correction d'une erreur dans le code causée par un réglage incorrect du nombre de plages. Merci à l'utilisateur @Elyees Abouda d' avoir signalé et suggéré une solution .
EDIT 2: Quelques petits refactoring et cas de bord fixe avec gestion manuelle des changements d'orientation. Merci à l'utilisateur @tatarize d' avoir signalé et suggéré une solution .
LayoutManager
travail de mettre les enfants dehors et pasRecyclerView
degetWidth()
ougetHeight()
vaut 0 avant la création de la vue, ce qui obtiendra un spanCount incorrect (1 puisque totalSpace sera <= 0). Ce que j'ai ajouté ignore setSpanCount dans ce cas. (onLayoutChildren
sera rappelé plus tard)J'ai accompli cela en utilisant un observateur d'arborescence de vues pour obtenir la largeur de la vue recyl une fois rendue, puis en obtenant les dimensions fixes de ma vue de carte à partir des ressources, puis en définissant le nombre de plages après avoir fait mes calculs. Elle n'est vraiment applicable que si les éléments que vous affichez ont une largeur fixe. Cela m'a aidé à remplir automatiquement la grille indépendamment de la taille ou de l'orientation de l'écran.
la source
ArrayIndexOutOfBoundsException
( à android.support.v7.widget.GridLayoutManager.layoutChunk (GridLayoutManager.java:361) ) lors du défilement du fichierRecyclerView
.removeGlobalOnLayoutListener()
est obsolète dans l'API niveau 16. utilisez à laremoveOnGlobalLayoutListener()
place. Documentation .Eh bien, c'est ce que j'ai utilisé, assez basique, mais fait le travail pour moi. Ce code obtient essentiellement la largeur de l'écran en creux, puis se divise par 300 (ou quelle que soit la largeur que vous utilisez pour la disposition de votre adaptateur). Ainsi, les téléphones plus petits avec une largeur de creux de 300 à 500 n'affichent qu'une seule colonne, les tablettes 2-3 colonnes, etc. Simple, sans tracas et sans inconvénient, pour autant que je puisse voir.
la source
J'ai étendu RecyclerView et remplacé la méthode onMeasure.
J'ai défini une largeur d'élément (variable membre) aussi tôt que possible, avec une valeur par défaut de 1. Cela met également à jour la configuration modifiée. Cela aura désormais autant de lignes que possible en mode portrait, paysage, téléphone / tablette, etc.
la source
Je publie ceci juste au cas où quelqu'un aurait une largeur de colonne étrange comme dans mon cas.
Je ne peux pas commenter la réponse de @ s-marks en raison de ma faible réputation. J'ai appliqué sa solution de solution mais j'ai eu une largeur de colonne étrange, j'ai donc modifié la fonction checkedColumnWidth comme suit:
En convertissant la largeur de colonne donnée en DP, le problème a été résolu.
la source
Pour accommoder le changement d'orientation sur la réponse de s-marks , j'ai ajouté une vérification sur le changement de largeur (largeur de getWidth (), pas largeur de colonne).
la source
La solution avec vote positif est correcte, mais gère les valeurs entrantes sous forme de pixels, ce qui peut vous faire trébucher si vous codez en dur des valeurs pour tester et supposer dp. Le moyen le plus simple est probablement de mettre la largeur de la colonne dans une dimension et de la lire lors de la configuration de GridAutofitLayoutManager, qui convertira automatiquement dp en valeur de pixel correcte:
la source
Lorsque vous créez GridLayoutManager, vous devez savoir combien de colonnes seront avec une taille minimale de imageView:
Après cela, vous devez redimensionner imageView dans l'adaptateur si vous avez de l'espace dans la colonne. Vous pouvez envoyer newImageViewSize puis inisilize adapter de l'activité là, vous calculez le nombre d'écran et de colonnes:
Cela fonctionne dans les deux orientations. En vertical, j'ai 2 colonnes et en horizontal - 4 colonnes. Le résultat: https://i.stack.imgur.com/WHvyD.jpg
la source
Je conclus ci-dessus les réponses ici
la source
C'est la classe de s.maks avec un correctif mineur pour le moment où la vue de recyclage elle-même change de taille. Par exemple, lorsque vous gérez vous-même les changements d'orientation (dans le manifeste
android:configChanges="orientation|screenSize|keyboardHidden"
), ou pour une autre raison, la vue recyclée peut changer de taille sans que mColumnWidth ne change. J'ai également changé la valeur int qu'il faut pour être la ressource de la taille et autorisé un constructeur sans ressource puis setColumnWidth à le faire vous-même.la source
Définissez spanCount sur un grand nombre (qui est le nombre maximal de colonnes) et définissez un SpanSizeLookup personnalisé sur GridLayoutManager.
C'est un peu moche, mais ça marche.
Je pense qu'un gestionnaire comme AutoSpanGridLayoutManager serait la meilleure solution, mais je n'ai rien trouvé de tel.
EDIT: Il y a un bug, sur certains appareils, il ajoute un espace vide à droite
la source
getSpanSize
retourne 3, il y aura un espace car vous ne remplissez pas le span.Voici les parties pertinentes d'un wrapper que j'ai utilisé pour détecter automatiquement le nombre de span. Vous l'initialisez en appelant
setGridLayoutManager
avec un R.layout.my_grid_item référence , et il détermine combien de ceux-ci peuvent tenir sur chaque ligne.la source