Przewiń RecyclerView, aby wyświetlić wybrany element na górze

216

Szukam sposobu przewijania a, RecyclerViewaby wyświetlić wybrany element na górze.

W sposób ListViewudało mi się to zrobić przy użyciu scrollTo(x,y)i coraz górną część elementu, które muszą być skoncentrowany.

Coś jak:

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

Problem polega na tym, że RecyclerViewzwraca błąd przy użyciu scrollTometody mówiąc

RecyclerView nie obsługuje przewijania do absolutnej pozycji

Jak mogę przewinąć a, RecyclerViewaby umieścić wybrany element u góry widoku?

Distwo
źródło

Odpowiedzi:

406

Jeśli używasz opcji LinearLayoutManagerlub naprzemiennie GridLayoutManager, każda z nich ma metodę scrollToPositionWithOffset , która przyjmuje zarówno pozycję, jak i przesunięcie początku elementu od początku RecyclerView, co wydaje się, że osiągnęłoby to, czego potrzebujesz (ustawienie przesunięcia na 0 powinien wyrównać się z górą).

Na przykład:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);
Niraj
źródło
34
Jeśli ktoś tego potrzebuje, aby przywrócić pozycję przewijania RecyclerView, oto jak zapisać pozycje przewijania (dwa argumenty dla metody scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); Zobacz v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM,
Wydaje mi się, że nie rozumiem, kiedy wywołać scrollToPosition? jeśli detektor wyboru znajduje się w poszczególnych widokach, w jaki sposób obiekt RecyclerView (który ma dostęp do swojego menedżera układu, który może wywoływać scrolToPosition) zostanie powiadomiony o wybraniu elementu?
Nonos
@Nosos możesz najpierw zapisać LayoutManager, którego używasz w RecylerView.setLayoutManager (), a następnie uzyskać do niego bezpośredni dostęp. Podobnie jak to: LinearLayoutManager llm = new LinearLayoutManager (this); mRecyclerView.setLayoutManager (llm); ... (później w kodzie) ... llm.scrollToPositionWithOffset (2, 20);
Niraj
19
Co jeśli chcę „płynnie” przewinąć do góry?
Tiago,
@Tiago, nie bawiłem się tym, ale ta 3-częściowa seria artykułów może pomóc: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj
93

Jeśli szukasz pionowego programu LinearLayout Manager, możesz uzyskać płynne przewijanie za pomocą niestandardowego 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;
        }
    }
}

użyj instancji menedżera układu w widoku recyklingu, a następnie wywołanie recyclerView.smoothScrollToPosition(pos);spowoduje płynne przewinięcie do wybranej pozycji do góry widoku recyklera

nizammoidu
źródło
1
Naprawdę przydatna odpowiedź! getHorizontalSnapPreference()Zastępuję również w TopSnappedSmoothScroller dla list poziomych.
Juan Saravia,
6
Czy mam rację, obserwując, że działa to tylko w przypadku przedmiotów, które nadal mają dość po / poniżej nich, aby wypełnić pozostałe RecyclerView? To znaczy: nie wydają się działać na ostatniej pozycji - niezależnie od SNAP_TO_STARTtych RecyclerViewtylko przewija aż element staje się widoczny na dole, ale nie przenieść go przez całą drogę w górę. Wszelkie pomysły, jak to osiągnąć? (Pracowałem nad tym, ustawiając niestandardowe wypełnienie na, RecyclerViewale to nie wydaje się naprawdę właściwe ...)
david.mihola
1
To samo w Kotlin => gist.github.com/Kevinrob/4f32a6b971930cdc49f06be8dce6403c
Kevin Robatel
czasami przedmiot się nie skupia, mam fokus, który można wyciągnąć dla widoku przedmiotu.
YLS
1
@ david.mihola Czy znalazłeś „właściwą” drogę? Ustawiłem niestandardowe wypełnienie w widoku recyklera, ale mam dziwne problemy ...
bernardo.g
52

// Przewiń pozycję poz

linearLayoutManager.scrollToPositionWithOffset(pos, 0);
HBQ
źródło
7
czy jest jakiś sposób, aby zrobić to płynnie?
Ashkan
1
@Ashkan stackoverflow.com/questions/31235183/…
Mark Buikema 20.04.2018
1
@MarkBuikema Znalazłem to wcześniej. dzięki za uwagę koleś
Ashkan
28

Musisz tylko zadzwonić recyclerview.scrollToPosition(position). W porządku!

Jeśli chcesz wywołać go w adapterze, po prostu pozwól, aby adapter miał instancję recyclinglerview lub działanie lub fragment, który zawiera recyclinglerview, niż implementuje getRecyclerview()w nich metodę .

Mam nadzieję, że ci to pomoże.

Zero
źródło
2
To zadziałało dla mnie - recyclinglerView.scrollToPosition (0); postaw mnie na szczycie.
Ray Hunter
2
że nie działa musisz użyć linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Anil Ugale
@Anil Ugale To nonsens. Oczywiście, że działa. Możesz użyć dowolnego LayoutManager dla RecyclerView. Dlaczego powinieneś się tym później przejmować, kiedy chcesz przewijać? Myślę, że robiłeś coś złego. Recyclerview.scrollToPosition (position) to wygodna metoda, która wywołuje LayoutManager.scrollToPosition (position).
Niesamowity
1
@TheincredibleJan nie działa we wszystkich przypadkach, layoutManager jest generalnie odpowiedzialny za przewijanie w każdym razie tylko dla twojej informacji.
Mohammed
11

to samo z regulatorem prędkości

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;
    }
}
}
użytkownik2212515
źródło
8

Spróbuj tego, co dla mnie zadziałało.

Utwórz zmienną private static int displayedposition = 0;

Teraz dla pozycji twojego RecyclerView w twojej działalności.

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


            }
        });

Umieść to oświadczenie w miejscu, w którym chcesz umieścić poprzednią witrynę wyświetlaną w twoim widoku.

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

Cóż, to wszystko, działało dobrze dla mnie \ o /

użytkownik3740244
źródło
7

po prostu wywołaj tę metodę po prostu:

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

zamiast:

recyclerView.scrollToPosition(yourItemPosition);
Amir Hossein Ghasemi
źródło
"zamiast"? Distwo nie wspominało o „scrollToPosition”. Gdyby go użył, nie byłoby problemu. scrollToPosition () zgodnie z przeznaczeniem.
Niesamowity
1
o dziwo, użycie scrollToPositionWithOffset działa dla mnie i recyclinglerview.scrollToPosition nie. Mimo to poprosiłbym Amira o zmianę RecyclerView na recyclinglerview, ponieważ daje to poczucie, że metody są statyczne, a nie są.
Mohit
6

co zrobiłem, aby przywrócić pozycję przewijania po odświeżeniu RecyclerView po kliknięciu przycisku:

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

pobranie przesunięcia pierwszego widocznego elementu od góry przed utworzeniem obiektu linearLayoutManager, a po jego utworzeniu wywołano scrollToPositionWithOffset obiektu LinearLayoutManager.

Tsiro
źródło
6

Nie wiem, dlaczego nie znalazłem najlepszej odpowiedzi, ale jest ona naprawdę prosta.

recyclerView.smoothScrollToPosition(position);

Bez błędów

Tworzy animacje

Lucem
źródło
gdzie umieścisz to w działaniu lub adapterze?
Arnold Brown
3

przewiń w określonej pozycji,
a to pomogło mi dużo. poprzez kliknięcie nasłuchiwacza możesz uzyskać pozycję w adapterze

layoutmanager.scrollToPosition(int position);
Sandeep Singh
źródło
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
źródło
3

Żadna z odpowiedzi nie wyjaśnia, jak wyświetlać ostatnie elementy na górze. Tak więc odpowiedzi działają tylko w przypadku przedmiotów, które wciąż mają wystarczającą liczbę elementów powyżej lub poniżej nich, aby wypełnić pozostałe RecyclerView. Na przykład, jeśli jest 59 elementów i wybrano 56. element, powinien on znajdować się na górze, jak na poniższym obrazku:

RecyclerView - wybrano element 56 Możemy obsłużyć tych przypadków, używając linearLayoutManager.scrollToPositionWithOffset(pos, 0)i dodatkową logikę w Adapterod RecyclerView- dodając niestandardowe margines poniżej ostatniego elementu (jeśli ostatni element nie jest widoczny to znaczy, nie ma wystarczająco dużo miejsca wypełnić RecyclerView). Niestandardowy margines może być różnicą między wysokością widoku głównego a wysokością elementu. Tak, Twój Adapterna RecyclerViewwyglądałby następująco:

...
@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
źródło
2

W moim przypadku RecyclerViewmam taki wyściełany top

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

Następnie, aby przewinąć element do góry, muszę

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());
Phan Van Linh
źródło
1

Jeśli LayoutManagerjest LinearLayoutManagerużyć scrollToPositionWithOffset(position,0);na nim i sprawi, że Twoja pozycja pierwszy widoczny element na liście. W przeciwnym razie, można użyć smoothScrollToPositionna RecyclerViewwprost.

Skończyło się na użyciu poniższego kodu.

 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
źródło
-2

Korzystam z poniższego kodu, aby płynnie przewijać element (thisView) do góry.
Działa również dla GridLayoutManager z widokami o różnych wysokościach:

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

Wydaje się, że działa wystarczająco dobrze, aby uzyskać szybkie rozwiązanie.

feheren.fekete
źródło
1
Jaka jest wartość thisPosition?
pRaNaY
jest to pozycja, którą chcesz płynnie przewinąć
Jan Rabe
Dlaczego nie użyć po prostu smoothScrollToPosition (pozycja wewnętrzna) bez żadnych obliczeń? Nie ma znaczenia, od czego zaczynasz, prawda?
Niesamowity Jan