Faites défiler RecyclerView pour afficher l'élément sélectionné en haut


216

Je cherche un moyen de faire défiler a RecyclerViewpour afficher l'élément sélectionné en haut.

Dans un, ListViewj'ai pu le faire en utilisant scrollTo(x,y)et en récupérant le haut de l'élément qui doit être centré.

Quelque chose comme:

@Override
public void onItemClick(View v, int pos){
    mylistView.scrollTo(0, v.getTop());
}

Le problème est que le RecyclerViewrenvoie une erreur lors de l'utilisation de sa scrollTométhode en disant

RecyclerView ne prend pas en charge le défilement vers une position absolue

Comment faire défiler a RecyclerViewpour placer l'élément sélectionné en haut de la vue?

Réponses:


406

Si vous utilisez le LinearLayoutManagerou Staggered GridLayoutManager, ils ont chacun une méthode scrollToPositionWithOffset qui prend à la fois la position et le décalage du début de l'élément depuis le début du RecyclerView, ce qui semble accomplir ce dont vous avez besoin (définir le décalage à 0 doit être aligné avec le haut).

Par exemple:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);

34
Si quelqu'un en a besoin pour restaurer la position de défilement d'un RecyclerView, voici comment enregistrer les positions de défilement (les deux arguments de la méthode scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); Afficher v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM

Ce que je ne semble pas comprendre, c'est quand appeler scrollToPosition? si l'écouteur de sélection se trouve sur les vues individuelles, comment l'objet RecyclerView (qui a accès à son gestionnaire de disposition qui peut appeler scrolToPosition) sera-t-il informé qu'un élément est sélectionné?
Nonos

@Nonos, vous pouvez d'abord stocker le LayoutManager que vous utilisez dans RecylerView.setLayoutManager (), puis y accéder directement. Comme ceci: LinearLayoutManager llm = new LinearLayoutManager (this); mRecyclerView.setLayoutManager (llm); ... (plus loin dans le code) ... llm.scrollToPositionWithOffset (2, 20);
Niraj

19
Et si je veux faire défiler vers le haut "en douceur"?
Tiago

@Tiago, je n'ai pas joué avec, mais cette série d'articles en 3 parties peut aider: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj

93

Si vous recherchez un gestionnaire LinearLayout vertical, vous pouvez obtenir un défilement fluide en utilisant une personnalisation LinearSmoothScroller:

import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;

public class SnappingLinearLayoutManager extends LinearLayoutManager {

    public SnappingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return SnappingLinearLayoutManager.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

utiliser une instance du gestionnaire de mise en page dans la vue de recyclage, puis appeler recyclerView.smoothScrollToPosition(pos);le défilement en douceur jusqu'à la position sélectionnée en haut de la vue du recycleur


1
Réponse vraiment utile! Je remplace également getHorizontalSnapPreference()les listes TopSnappedSmoothScroller for Horizontal.
Juan Saravia

6
Ai-je raison de remarquer que cela ne fonctionne que pour les articles qui ont encore suffisamment après / en dessous pour remplir le reste RecyclerView? Ce qui veut dire: Cela ne semble pas fonctionner pour le dernier élément - quelles que soient les SNAP_TO_STARTles RecyclerViewseuls rouleaux jusqu'à ce que l'élément devient visible au fond , mais ne se déplace pas sur toute la hauteur. Des idées sur la façon d'y parvenir? (J'ai travaillé autour de cela en définissant un rembourrage personnalisé sur le RecyclerViewmais cela ne semble pas vraiment correct ...)
david.mihola


Parfois, le dosent de l'élément se concentre, j'ai un focus dessinable pour la vue de l'élément.
YLS

1
@ david.mihola Avez-vous trouvé le bon chemin? J'ai défini un rembourrage personnalisé pour ma vue de recyclage, mais j'ai des problèmes étranges ...
bernardo.g


27

Vous avez juste besoin d'appeler recyclerview.scrollToPosition(position). C'est très bien!

Si vous voulez l'appeler dans l'adaptateur, laissez simplement votre adaptateur avoir l'instance de recyclerview ou l'activité ou le fragment qui contient recyclerview, puis implémente la méthode getRecyclerview()en eux.

J'espère que cela peut vous aider.


2
Cela a fonctionné pour moi - recyclerView.scrollToPosition (0); mettez-moi au sommet.
Ray Hunter

2
que ne fonctionne pas, vous devez utiliser le linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Anil Ugale

@Anil Ugale C'est absurde. Bien sûr, cela fonctionne. Vous pouvez utiliser n'importe quel LayoutManager pour une RecyclerView. Pourquoi devriez-vous vous en soucier plus tard lorsque vous souhaitez faire défiler? Je pense que tu faisais quelque chose de mal. Recyclerview.scrollToPosition (position) est une méthode pratique qui appelle LayoutManager.scrollToPosition (position).
L'incroyable

1
@TheincredibleJan cela ne fonctionne pas dans tous les cas, le layoutManager est généralement responsable du défilement de toute façon juste pour vos informations.
Mohammed

Cela ne met pas toujours l'élément demandé en haut comme la question posée.
James Riordan

11

idem avec régulateur de vitesse

public class SmoothScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 110f;
private Context mContext;

public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) {
    super(context,orientation,reverseLayout);
    mContext = context;
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                   int position) {
    RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){
        //This controls the direction in which smoothScroll looks for your view
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return new PointF(0, 1);
        }

        //This returns the milliseconds it takes to scroll one pixel.
        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
        }
    };
    smoothScroller.setTargetPosition(position);
    startSmoothScroll(smoothScroller);
}


private class TopSnappedSmoothScroller extends LinearSmoothScroller {
    public TopSnappedSmoothScroller(Context context) {
        super(context);

    }

    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        return SmoothScrollLinearLayoutManager.this
                .computeScrollVectorForPosition(targetPosition);
    }

    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;
    }
}
}

8

Essayez ce qui a fonctionné pour moi cool!

Créer une variable private static int displayedposition = 0;

Maintenant, pour la position de votre RecyclerView dans votre activité.

myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();


                displayedposition = llm.findFirstVisibleItemPosition();


            }
        });

Placez cette déclaration à l'endroit où vous souhaitez qu'elle place l'ancien site affiché dans votre vue.

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        llm.scrollToPositionWithOffset(displayedposition , youList.size());

Eh bien c'est ça, ça a bien fonctionné pour moi \ o /


7

il suffit d'appeler cette méthode simplement:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);

au lieu de:

recyclerView.scrollToPosition(yourItemPosition);

"au lieu de"? Distwo n'a pas mentionné "scrollToPosition". S'il l'avait utilisé, il n'y aurait pas eu de problème. scrollToPosition () comme prévu.
L'incroyable

1
étrangement, l'utilisation de scrollToPositionWithOffset fonctionne pour moi et recyclerview.scrollToPosition n'a pas fonctionné. Cependant, je demanderais à Amir de changer RecyclerView en recyclerview, car cela donne un sentiment que les méthodes sont statiques, et elles ne le sont pas.
Mohit

6

ce que j'ai fait pour restaurer la position de défilement après avoir actualisé le bouton RecyclerView sur cliqué:

if (linearLayoutManager != null) {

    index = linearLayoutManager.findFirstVisibleItemPosition();
    View v = linearLayoutManager.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
    Log.d("TAG", "visible position " + " " + index);
}

else{
    index = 0;
}

linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.scrollToPositionWithOffset(index, top);

obtenir le décalage du premier élément visible par le haut avant de créer l'objet linearLayoutManager et après l'avoir instancié, scrollToPositionWithOffset de l'objet LinearLayoutManager a été appelé.


6

Je ne sais pas pourquoi je n'ai pas trouvé la meilleure réponse mais c'est vraiment simple.

recyclerView.smoothScrollToPosition(position);

Aucune erreur

Crée des animations


où allez-vous mettre cela dans l'activité ou l'adaptateur?
Arnold Brown

3

faites défiler à une position particulière
et cela m'a beaucoup aidé. par un écouteur de clic, vous pouvez obtenir la position dans votre adaptateur

layoutmanager.scrollToPosition(int position);

3
If you want to scroll automatic without show scroll motion then you need to write following code:

**=> mRecyclerView.getLayoutManager().scrollToPosition(position);**

If you want to display scroll motion then you need to add following code.
=>Step 1: You need to declare SmoothScroller.

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };

=>step 2: You need to add this code any event you want to perform scroll to specific position.
=>First you need to set target position to SmoothScroller.

**smoothScroller.setTargetPosition(position);**

=>Then you need to set SmoothScroller to LayoutManager.
                        **mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);**

3

Aucune des réponses n'explique comment afficher les derniers éléments en haut. Ainsi, les réponses ne fonctionnent que pour les éléments qui ont encore suffisamment d'éléments au-dessus ou en dessous pour remplir le reste RecyclerView. Par exemple, s'il y a 59 éléments et qu'un 56e est sélectionné, il devrait être en haut comme dans l'image ci-dessous:

RecyclerView - l'élément 56 est sélectionné Nous pourrions gérer ces cas en utilisant linearLayoutManager.scrollToPositionWithOffset(pos, 0)et une logique supplémentaire dans Adapterof RecyclerView- en ajoutant une marge personnalisée sous le dernier élément (si le dernier élément n'est pas visible, cela signifie qu'il y a suffisamment d'espace pour remplir le RecyclerView). La marge personnalisée peut être une différence entre la hauteur de la vue racine et la hauteur de l'élément. Ainsi, votre Adapterfor RecyclerViewressemblerait à ceci:

...
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    ...

    int bottomHeight = 0;
    int itemHeight = holder.itemView.getMeasuredHeight();
    // if it's the last item then add a bottom margin that is enough to bring it to the top
    if (position == mDataSet.length - 1) {
        bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight);
    }
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
    params.setMargins(0, 0, params.rightMargin, bottomHeight);
    holder.itemView.setLayoutParams(params);

    ...
} 
...

2

Dans mon cas, j'ai RecyclerViewun rembourrage comme celui-ci

<android.support.v7.widget.RecyclerView
     ...
     android:paddingTop="100dp"
     android:clipToPadding="false"
/>

Ensuite, pour faire défiler un élément vers le haut, je dois

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());

1

Si votre LayoutManagerest LinearLayoutManagerutilisez - vous scrollToPositionWithOffset(position,0);sur elle et il fera de votre point le premier élément visible dans la liste. Dans le cas contraire, vous pouvez utiliser smoothScrollToPositionle RecyclerViewdirectement.

J'ai fini par utiliser le code ci-dessous.

 RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager();
        if (layoutManager instanceof LinearLayoutManager) {
            // Scroll to item and make it the first visible item of the list.
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
        } else {
            mainList.smoothScrollToPosition(position);
        }

-2

J'utilise le code ci-dessous pour faire défiler en douceur un élément (thisView) vers le haut.
Il fonctionne également pour GridLayoutManager avec des vues de différentes hauteurs:

View firstView = mRecyclerView.getChildAt(0);
int toY = firstView.getTop();
int firstPosition = mRecyclerView.getChildAdapterPosition(firstView);
View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition);
int fromY = thisView.getTop();

mRecyclerView.smoothScrollBy(0, fromY - toY);

Semble fonctionner suffisamment bien pour une solution rapide.


1
Quelle est la valeur de thisPosition?
2016

c'est la position à laquelle vous souhaitez lisser le défilement
Jan Rabe

Pourquoi ne pas simplement utiliser smoothScrollToPosition (int position) sans aucun calcul? Peu importe d'où vous commencez, n'est-ce pas?
L'incroyable
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.