Scrollen Sie durch RecyclerView, um das ausgewählte Element oben anzuzeigen


215

Ich suche nach einer Möglichkeit, einen Bildlauf durchzuführen RecyclerView, um das ausgewählte Element oben anzuzeigen.

In a konnte ListViewich dies tun, indem scrollTo(x,y)ich die Oberseite des Elements, das zentriert werden muss, verwendete und abrief.

Etwas wie:

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

Das Problem ist, dass das RecyclerVieweinen Fehler zurückgibt, wenn es seine scrollToMethode verwendet, die sagt

RecyclerView unterstützt das Scrollen zu einer absoluten Position nicht

Wie kann ich einen Bildlauf durchführen RecyclerView, um das ausgewählte Element oben in der Ansicht zu platzieren?

Antworten:


405

Wenn Sie die OptionLinearLayoutManager oder Versetzt verwenden GridLayoutManager, verfügen sie jeweils über eine scrollToPositionWithOffset- Methode, die sowohl die Position als auch den Versatz des Starts des Elements vom Anfang des verwendet. DiesRecyclerView scheint das zu erreichen, was Sie benötigen (Setzen des Versatzes auf 0) sollte mit der Oberseite ausgerichtet sein).

Zum Beispiel:

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

34
Wenn jemand dies zum Wiederherstellen der Bildlaufposition einer RecyclerView benötigt, können Sie auf diese Weise die Bildlaufpositionen speichern (die beiden Argumente für die Methode scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); View v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM

Was ich anscheinend nicht verstehe, ist, wann scrollToPosition aufgerufen werden soll? Wenn sich der Auswahllistener in den einzelnen Ansichten befindet, wie wird das RecyclerView-Objekt (das Zugriff auf seinen Layout-Manager hat, der scrolToPosition aufrufen kann) benachrichtigt, dass ein Element ausgewählt ist?
Nonos

@Nonos Sie können zuerst den LayoutManager, den Sie verwenden, in RecylerView.setLayoutManager () speichern und dann direkt darauf zugreifen. So: LinearLayoutManager llm = neuer LinearLayoutManager (this); mRecyclerView.setLayoutManager (llm); ... (später im Code) ... llm.scrollToPositionWithOffset (2, 20);
Niraj

19
Was ist, wenn ich "reibungslos" nach oben scrollen möchte?
Tiago

@Tiago, ich habe nicht damit herumgespielt, aber diese dreiteilige Artikelserie kann helfen: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj

93

Wenn Sie nach vertikalem LinearLayout Manager suchen, können Sie mit einem benutzerdefinierten Objektiv einen reibungslosen Bildlauf erzielen 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;
        }
    }
}

Verwenden Sie eine Instanz des Layoutmanagers in der Recyclingansicht. Wenn Sie dann aufrufen, recyclerView.smoothScrollToPosition(pos);wird der Bildlauf an die ausgewählte Position zum oberen Rand der Recyclingansicht verschoben


1
Wirklich nützliche Antwort! Ich überschreibe auch getHorizontalSnapPreference()im TopSnappedSmoothScroller für horizontale Listen.
Juan Saravia

6
Habe ich Recht, wenn ich beobachte, dass dies nur für Elemente funktioniert, die nach / unter noch genug haben, um die verbleibenden zu füllen RecyclerView? Welches ist zu sagen: Das ist nicht das letzte Element zu funktionieren scheint - unabhängig von den SNAP_TO_STARTdie RecyclerViewnur scrollt , bis das Element am unteren Rand sichtbar wird , aber bis es nicht den ganzen Weg bewegen. Irgendwelche Ideen, wie dies erreicht werden kann? (Ich habe dies RecyclerView
umgangen,


Irgendwann wird das Objekt fokussiert, ich habe einen Fokus, der für die Objektansicht gezeichnet werden kann.
YLS

1
@ david.mihola Hast du einen "richtigen" Weg gefunden? Ich habe benutzerdefinierte Polsterung für meine Recycling-Ansicht festgelegt, aber ich habe einige seltsame Probleme ...
bernardo.g


28

Sie müssen nur anrufen recyclerview.scrollToPosition(position) . Das ist gut!

Wenn Sie es im Adapter aufrufen möchten, lassen Sie Ihren Adapter einfach die Instanz von recyclerview oder die Aktivität oder das Fragment haben, die recyclerview enthält, und implementieren dann die Methode getRecyclerview() in ihnen.

Ich hoffe es kann dir helfen.


2
Das hat bei mir funktioniert - recyclerView.scrollToPosition (0); Setzen Sie mich an die Spitze.
Ray Hunter

2
Wenn dies nicht funktioniert, müssen Sie den linearLayoutManager.scrollToPositionWithOffset (pos, 0) verwenden.
Anil Ugale

@ Anil Ugale Das ist Unsinn. Natürlich funktioniert es. Sie können jeden LayoutManager für eine RecyclerView verwenden. Warum sollten Sie sich später darum kümmern, wenn Sie scrollen möchten? Ich denke du hast etwas falsch gemacht. Recyclerview.scrollToPosition (Position) ist eine praktische Methode, die LayoutManager.scrollToPosition (Position) aufruft.
Der unglaubliche

1
@TheincredibleJan es funktioniert nicht in allen Fällen, der layoutManager ist in der Regel ohnehin nur zu Ihrer Information für das Scrollen verantwortlich.
Mohammed

11

Gleiches gilt für den Geschwindigkeitsregler

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

Probieren Sie aus, was für mich cool funktioniert hat!

Erstellen Sie eine Variable private static int displayedposition = 0;

Nun zur Position Ihres RecyclerView in Ihrer Aktivität.

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();


            }
        });

Platzieren Sie diese Anweisung an der Stelle, an der die zuvor in Ihrer Ansicht angezeigte Site platziert werden soll.

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

Nun, das war's, es hat gut funktioniert für mich \ o /


7

Rufen Sie diese Methode einfach auf:

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

anstatt:

recyclerView.scrollToPosition(yourItemPosition);

"anstatt"? Distwo erwähnte "scrollToPosition" nicht. Wenn er es benutzt hätte, hätte es kein Problem gegeben. scrollToPosition () wie vorgesehen.
Der unglaubliche

1
Seltsamerweise funktioniert die Verwendung von scrollToPositionWithOffset für mich und recyclerview.scrollToPosition nicht. Obwohl ich Amir bitten würde, die RecyclerView in recyclerview zu ändern, da dies den Eindruck erweckt, dass die Methoden statisch sind und nicht.
Mohit

6

Was ich getan habe, um die Bildlaufposition wiederherzustellen, nachdem die Schaltfläche RecyclerView on aktualisiert wurde?

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);

Abrufen des Versatzes des ersten sichtbaren Elements von oben vor dem Erstellen des linearLayoutManager-Objekts und Aufrufen des scrollToPositionWithOffset des LinearLayoutManager-Objekts nach dem Instanziieren.


6

Ich weiß nicht, warum ich nicht die beste Antwort gefunden habe, aber es ist wirklich einfach.

recyclerView.smoothScrollToPosition(position);

Keine Fehler

Erstellt Animationen


Wo werden Sie dies in Aktivität oder Adapter setzen?
Arnold Brown

3


Scrollen Sie an einer bestimmten Position und das hat mir sehr geholfen. Durch Klicken auf Listener können Sie die Position in Ihrem Adapter erhalten

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

Keine der Antworten erklärt, wie die letzten Elemente oben angezeigt werden. Die Antworten funktionieren also nur für Elemente, über oder unter denen noch genügend Elemente vorhanden sind, um die verbleibenden zu füllen RecyclerView. Wenn beispielsweise 59 Elemente vorhanden sind und ein 56. Element ausgewählt ist, sollte es wie in der folgenden Abbildung oben angezeigt werden:

RecyclerView - Element 56 ist ausgewählt Wir könnten diese Fälle behandeln, indem wir linearLayoutManager.scrollToPositionWithOffset(pos, 0)eine zusätzliche Logik in Adapterof verwenden RecyclerView- indem wir einen benutzerdefinierten Rand unter dem letzten Element hinzufügen (wenn das letzte Element nicht sichtbar ist, bedeutet dies, dass genügend Platz vorhanden ist RecyclerView). Der benutzerdefinierte Rand kann ein Unterschied zwischen der Höhe der Stammansicht und der Höhe des Elements sein. Ihr Adapterfor RecyclerViewwürde also wie folgt aussehen:

...
@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

In meinem Fall habe RecyclerViewich so ein Polsteroberteil

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

Dann muss ich zum Scrollen eines Elements nach oben

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

1

Wenn Du LayoutManagerbist LinearLayoutManagerverwenden Sie scrollToPositionWithOffset(position,0);darauf und es wird Ihren Artikel das erste sichtbare Element in der Liste machen. Ansonsten können Sie smoothScrollToPositionauf dem verwendenRecyclerView direkt verwenden.

Am Ende habe ich den folgenden Code verwendet.

 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

Ich verwende den folgenden Code, um ein Element (thisView) reibungslos nach oben zu scrollen.
Es funktioniert auch für GridLayoutManager mit Ansichten unterschiedlicher Höhe:

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);

Scheint gut genug für eine schnelle Lösung zu funktionieren.


1
Was ist der Wert von thisPosition?
pRaNaY

Es ist die Position, zu der Sie glatt scrollen möchten
Jan Rabe

Warum nicht einfach smoothScrollToPosition (int position) ohne Berechnung verwenden? Egal wo du anfängst, oder?
Der unglaubliche
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.