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?

Distwo
quelle

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

Nizammoidu
quelle
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,
1
Gleiches in Kotlin => gist.github.com/Kevinrob/4f32a6b971930cdc49f06be8dce6403c
Kevin Robatel
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
52

// Bildlaufposition pos

linearLayoutManager.scrollToPositionWithOffset(pos, 0);
HBQ
quelle
7
Gibt es eine Möglichkeit, dies reibungslos zu tun?
Ashkan
1
@ MarkBuikema Ich habe es schon einmal gefunden. Danke für deine Aufmerksamkeit, Alter
Ashkan
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.

Null
quelle
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;
    }
}
}
user2212515
quelle
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 /

user3740244
quelle
7

Rufen Sie diese Methode einfach auf:

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

anstatt:

recyclerView.scrollToPosition(yourItemPosition);
Amir Hossein Ghasemi
quelle
"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.

Tsiro
quelle
6

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

recyclerView.smoothScrollToPosition(position);

Keine Fehler

Erstellt Animationen

Lucem
quelle
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);
Sandeep Singh
quelle
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);**
Paras Santoki
quelle
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);

    ...
} 
...
Anatolii
quelle
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());
Phan Van Linh
quelle
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);
        }
Mohamed Nageh
quelle
-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.

feheren.fekete
quelle
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