Scorri RecyclerView per visualizzare l'elemento selezionato in alto


216

Sto cercando un modo per scorrere a RecyclerViewper mostrare l'elemento selezionato in alto.

In un ListViewho potuto farlo usando scrollTo(x,y)e ottenendo la parte superiore dell'elemento che deve essere centrato.

Qualcosa di simile a:

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

Il problema è che RecyclerViewrestituisce un errore quando si usa il scrollTometodo dicendo

RecyclerView non supporta lo scorrimento in una posizione assoluta

Come posso scorrere a RecyclerViewper posizionare l'elemento selezionato nella parte superiore della vista?

Risposte:


406

Se stai usando LinearLayoutManagero Sfalsato GridLayoutManager, ognuno di essi ha un metodo scrollToPositionWithOffset che prende sia la posizione che l'offset dell'inizio dell'elemento dall'inizio del RecyclerView, il che sembra che realizzerebbe ciò di cui hai bisogno (impostando l'offset su 0 dovrebbe allinearsi con la parte superiore).

Per esempio:

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

34
Se qualcuno ne ha bisogno per ripristinare la posizione di scorrimento di un RecyclerView, ecco come salvare le posizioni di scorrimento (i due argomenti per il metodo scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); Visualizza v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM,

Quello che non mi sembra di capire è quando chiamare scrollToPosition? se il listener di selezione si trova nelle singole viste, come verrà notificato l'oggetto RecyclerView (che ha accesso al suo gestore di layout che può chiamare scrolToPosition) che un elemento è selezionato?
Nonos,

@Nonos è innanzitutto possibile memorizzare il LayoutManager che si utilizza in RecylerView.setLayoutManager () e quindi accedervi direttamente. In questo modo: LinearLayoutManager llm = new LinearLayoutManager (this); mRecyclerView.setLayoutManager (LLM); ... (più avanti nel codice) ... llm.scrollToPositionWithOffset (2, 20);
Niraj,

19
E se volessi scorrere verso l'alto "senza intoppi"?
Tiago,

@Tiago, non ci ho giocato, ma questa serie di articoli in 3 parti può essere d'aiuto: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj

93

Se stai cercando LinearLayout Manager verticale puoi ottenere uno scorrimento uniforme usando un personalizzato 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;
        }
    }
}

usa un'istanza del layoutmanager nella vista di riciclo e poi chiamando recyclerView.smoothScrollToPosition(pos); scorrerà senza intoppi fino alla posizione selezionata in cima alla vista del riciclo


1
Risposta davvero utile! Ho anche getHorizontalSnapPreference()la precedenza negli elenchi TopSnappedSmoothScroller per gli elenchi orizzontali.
Juan Saravia,

6
Ho ragione nell'osservare che questo funziona solo per gli articoli che hanno ancora abbastanza dopo / sotto di loro per riempire il rimanente RecyclerView? Vale a dire: questo non sembra funzionare per l'ultimo elemento, indipendentemente SNAP_TO_STARTdagli RecyclerViewunici rotoli finché l'elemento non diventa visibile in basso ma non lo sposta completamente verso l'alto. Qualche idea su come raggiungere questo obiettivo? (Ho RecyclerView
risolto il


a volte il dosatore dell'oggetto si concentra, sto avendo un focus disegnabile per la visualizzazione dell'oggetto.
YLS

1
@ david.mihola Hai trovato il modo "giusto"? Ho impostato il padding personalizzato sul mio recyclerview ma sto riscontrando alcuni strani problemi ...
bernardo.g


27

Devi solo chiamare recyclerview.scrollToPosition(position). Va bene!

Se vuoi chiamarlo nell'adattatore, lascia che l'adattatore abbia l'istanza di recyclerview o l'attività o il frammento che contiene recyclerview, quindi implementa il metodo getRecyclerview()in essi.

Spero possa aiutarti.


2
Questo ha funzionato per me - recyclerView.scrollToPosition (0); mettimi in cima.
Ray Hunter,

2
che non funzionante devi usare linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Anil Ugale,

@Anil Ugale È una sciocchezza. Certo che funziona. È possibile utilizzare qualsiasi LayoutManager per un RecyclerView. Perché in seguito dovresti preoccupartene quando vuoi scorrere? Penso che tu stia facendo qualcosa di sbagliato. Recyclerview.scrollToPosition (position) è un metodo pratico che chiama LayoutManager.scrollToPosition (position).
L'incredibile gennaio

1
@TheincredibleJan non funziona in tutti i casi, layoutManager è generalmente responsabile dello scorrimento comunque solo per le tue informazioni.
Mohammed,

Questo non mette sempre l'elemento richiesto in cima come la domanda posta.
James Riordan

11

lo stesso con il regolatore di velocità

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

Prova cosa ha funzionato per me alla grande!

Crea una variabile private static int displayedposition = 0;

Ora per la posizione di RecyclerView nella tua attività.

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


            }
        });

Inserisci questa dichiarazione nel punto in cui desideri posizionare il sito precedente visualizzato nella visualizzazione.

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

Bene, questo è tutto, ha funzionato bene per me \ o /


7

chiama semplicemente questo metodo semplicemente:

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

invece di:

recyclerView.scrollToPosition(yourItemPosition);

"invece di"? Distwo non ha menzionato "scrollToPosition". Se l'avesse usato non ci sarebbero stati problemi. scrollToPosition () come previsto.
L'incredibile gennaio

1
stranamente, usando scrollToPositionWithOffset funziona per me e recyclerview.scrollToPosition no. Tuttavia, richiederei ad Amir di modificare RecyclerView in recyclerview, poiché ha la sensazione che i metodi siano statici e non lo siano.
Mohit,

6

cosa ho fatto per ripristinare la posizione di scorrimento dopo aver aggiornato il pulsante RecyclerView sul clic:

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

ottenere l'offset del primo elemento visibile dall'alto prima di creare l'oggetto linearLayoutManager e dopo averlo creato un'istanza è stato chiamato scrollToPositionWithOffset dell'oggetto LinearLayoutManager.


6

Non so perché non ho trovato la risposta migliore ma è davvero semplice.

recyclerView.smoothScrollToPosition(position);

Nessun errore

Crea animazioni


dove lo metterai in attività o adattatore?
Arnold Brown,

3

scorrere in una posizione particolare
e questo mi ha aiutato molto. facendo clic su listener puoi ottenere la posizione nel tuo adattatore

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

Nessuna delle risposte spiega come mostrare gli ultimi elementi nella parte superiore. Quindi, le risposte funzionano solo per gli oggetti che hanno ancora abbastanza oggetti sopra o sotto di loro per riempire il rimanente RecyclerView. Ad esempio, se sono presenti 59 elementi e viene selezionato un 56 ° elemento, questo dovrebbe essere in alto come nella figura seguente:

RecyclerView - l'elemento 56 è selezionato Potremmo gestire questi casi utilizzando linearLayoutManager.scrollToPositionWithOffset(pos, 0)e una logica aggiuntiva in Adapterof RecyclerView- aggiungendo un margine personalizzato sotto l'ultimo elemento (se l'ultimo elemento non è visibile, significa che c'è abbastanza spazio riempire il RecyclerView). Il margine personalizzato potrebbe fare la differenza tra l'altezza della vista radice e l'altezza dell'elemento. Quindi, il tuo Adapterper RecyclerViewdovrebbe apparire come segue:

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

Nel mio caso RecyclerViewho un top imbottito come questo

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

Quindi per scorrere un elemento verso l'alto, devo farlo

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

1

Se la vostra LayoutManagerè LinearLayoutManagersi utilizza scrollToPositionWithOffset(position,0);su di esso e renderà il vostro articolo il primo elemento visibile nell'elenco. Altrimenti, puoi usare smoothScrollToPositionsulRecyclerView direttamente.

Ho finito con il seguente codice.

 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

Uso il codice qui sotto per scorrere uniformemente un elemento (thisView) verso l'alto.
Funziona anche con GridLayoutManager con viste di diverse altezze:

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

Sembra funzionare abbastanza bene per una soluzione rapida.


1
Qual è il valore di thisPosition?
pRaNaY,

è la posizione in cui vuoi scorrere uniformemente
Jan Rabe

Perché non usare semplicemente smoothScrollToPosition (int position) senza alcun calcolo? Non importa da dove inizi, vero?
L'incredibile gennaio
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.