web-dev-qa-db-fra.com

Hauteur du pouce de défilement rapide RecyclerView trop petite pour un grand ensemble de données

J'utilise le défilement rapide RecyclerView par défaut et j'ai suivi ce guide pour le prendre en charge.

Maintenant, le problème est que le pouce redimensionne sa hauteur en fonction de la taille de l'ensemble de données. Pour les gros articles comme 100 et plus, le pouce devient très petit et il devient presque difficile de réagir au traîner.

S'il vous plaît, pouvez-vous définir une hauteur minimale pour le pouce de défilement rapide?.

11
X09

Ce n'est qu'une réponse partielle; Il me manque (au moins) une pièce du puzzle, mais j'espère que quelqu'un d'autre pourra le comprendre.

Une fois que vous avez ajouté les attributs nécessaires à votre balise <RecyclerView> (comme indiqué dans la réponse liée à la question de OP), le dimensionnement/positionnement de la barre de défilement est contrôlé par trois méthodes à l'intérieur de LinearLayoutManager

  • int computeVerticalScrollRange(): La taille de la piste de la barre de défilement.

  • int computeVerticalScrollExtent(): La taille du pouce de la barre de défilement.

  • int computeVerticalScrollOffset(): La distance entre le haut de la piste de la barre de défilement et le haut de son pouce.

Les unités pour ces méthodes sont arbitraires; vous pouvez utiliser ce que vous voulez tant que les trois méthodes partagent les mêmes unités. Par défaut, LinearLayoutManager utilisera l’un des deux ensembles d’unités suivants:

  • mSmoothScrollbarEnabled == true: utilisez des unités basées sur la taille en pixels des éléments visibles dans RecyclerView.

  • mSmoothScrollbarEnabled == false: utilisez des unités basées sur les positions des éléments visibles dans l'adaptateur de RecyclerView.

Pour contrôler la taille du pouce de la barre de défilement vous-même, vous devrez remplacer ces méthodes ... mais voici l'élément qui me manque: Dans toutes mes expériences, computeVerticalScrollExtent() n'est jamais appelé par le système. Cela dit, nous pouvons encore montrer des progrès ici.

Tout d'abord, j'ai créé un adaptateur simple qui affiche 500 CardViews avec la position de l'élément à l'intérieur. J'ai activé le défilement rapide avec des dessins vraiment simples (mais laids). Voici à quoi ressemble la barre de défilement avec seulement une implémentation par défaut de LinearLayoutManager:

 enter image description here

Comme vous l'avez constaté, avec 500 (petits) éléments, le pouce de la barre de défilement est vraiment petit et assez difficile à exploiter. Nous pouvons agrandir considérablement la barre de défilement en surchargeant computeVerticalScrollRange() pour ne renvoyer qu'une constante fixe ... J'ai choisi 5000 essentiellement au hasard, juste pour afficher le changement majeur:

 enter image description here

Bien sûr, la barre de défilement ne fonctionne pas comme prévu; faire défiler la liste en le faisant glisser comme d'habitude déplace le pouce beaucoup plus qu'il ne le devrait, et un défilement rapide en faisant glisser le curseur déplace la liste beaucoup moins qu'il ne le devrait.

Sur mon appareil, avec la plage choisie au hasard de 5000, la substitution de computeVerticalScrollOffset() comme suit fait déplacer le curseur de la barre de défilement parfaitement lorsque je fais défiler la liste en le faisant glisser:

@Override
public int computeVerticalScrollRange(RecyclerView.State state) {
    return 5000;
}

@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
    return (int) (super.computeVerticalScrollOffset(state) / 23.5f);
}

Cependant, cela ne résout toujours pas le deuxième problème: faire glisser le pouce lui-même ne fait pas défiler correctement la liste. Comme je l'ai mentionné ci-dessus, il semblerait que la bonne chose à faire ici serait de remplacer computeVerticalScrollExtent(), mais le système n'invoque jamais cette méthode. Je l'ai même annulée pour simplement lancer une exception et mon application ne se bloque jamais.

Espérons que cela aide au moins les gens à aller dans la bonne direction pour une solution complète.

PS: Les implémentations de computeVerticalScrollRange() et computeVerticalScrollOffset() que j'ai incluses dans cette réponse sont intentionnellement simples (lire: fictif). Les "vraies" implémentations seraient beaucoup plus complexes; Les implémentations par défaut de LinearLayoutManager prennent en compte l'orientation du périphérique, les premier et dernier éléments visibles de la liste, le nombre d'éléments hors écran dans les deux sens, le défilement régulier, divers indicateurs de disposition, etc.

3
Ben P.

Il s’agit d’un problème connu, ouvert en août 2017: https://issuetracker.google.com/issues/64729576

J'attends toujours des recommandations sur la façon de gérer RecyclerView avec un défilement rapide sur de grandes quantités de données. Aucune réponse de Google sur cette question jusqu'à présent sur les recommandations :(

La réponse de Nabil est similaire à une solution de contournement mentionnée dans ce numéro. La réponse de Nabil copie la classe FastScroller et la modifie pour assurer une taille minimale du pouce. La solution de contournement contenue dans le problème étend FastScroller (mais elle doit rester dans le package Android.support.v7.widget) pour garantir la taille minimale du pouce.

1
Carmen

J'ai résolu ce problème en copiant les classes Fast Scroller à partir de Android.support.v7.widget.FastScroller

Ensuite, j'ai supprimé le défilement rapide activé du xml et appliqué fastscroller à l'aide des codes ci-dessous.

StateListDrawable verticalThumbDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.fastscroll_sunnah);
Drawable verticalTrackDrawable = getResources().getDrawable(R.drawable.fastscroll_line_drawable);
StateListDrawable horizontalThumbDrawable = (StateListDrawable)getResources().getDrawable(R.drawable.fastscroll_sunnah);
Drawable horizontalTrackDrawable = getResources().getDrawable(R.drawable.fastscroll_line_drawable);

Resources resources = getContext().getResources();
new FastScroller(recyclerView, verticalThumbDrawable, verticalTrackDrawable,
                horizontalThumbDrawable, horizontalTrackDrawable,
                resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
                resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
                resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));

À l'intérieur de la classe FastScroller, j'ai étendu defaultWidth 

FastScroller(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable,
        Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
        Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange,
        int margin) {
    ...
    this.defaultWidth = defaultWidth;
    ...

Puis j'ai mis à jour le code avec cette méthode

void updateScrollPosition(int offsetX, int offsetY) {
...
mVerticalThumbHeight = Math.max(defaultWidth * 4, Math.min(verticalVisibleLength,
                (verticalVisibleLength * verticalVisibleLength) / verticalContentLength));
...
...
mHorizontalThumbWidth = Math.max(defaultWidth * 4, Math.min(horizontalVisibleLength,
                (horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength));
...    
}

Cela garantit que la hauteur/largeur minimale du pouce est égale à 4 fois la largeur par défaut.