Jak sprawdzić, czy plik RecyclerView / LinearLayoutManager jest przewijany do góry czy do dołu?

81

Obecnie używam poniższego kodu, aby sprawdzić, czy SwipeRefreshLayout powinno być włączone.

private void laySwipeToggle() {
    if (mRecyclerView.getChildCount() == 0 || mRecyclerView.getChildAt(0).getTop() == 0) {
        mLaySwipe.setEnabled(true);
    } else {
        mLaySwipe.setEnabled(false);
    }
}

Ale tu jest problem. Po przewinięciu do granicy widoku innego elementu mRecyclerView.getChildAt(0).getTop()również zwraca 0.

Problem

Czy jest coś takiego jak RecyclerView.isScrolledToBottom()lub RecyclerView.isScrolledToTop()?

EDYCJA: (mRecyclerView.getChildAt(0).getTop() == 0 && linearLayoutManager.findFirstVisibleItemPosition() == 0)niby robi RecyclerView.isScrolledToTop(), ale o co chodzi RecyclerView.isScrolledToBottom()?

Saren Arterius
źródło
Przypuszczam, że to drugie można osiągnąć, sprawdzając dolną część widoku recyklingu względem ostatniego dziecka, tj. Coś w wierszach mRecyclerView.getBottom () == linearLayoutmanager.findViewbyPosition (adapter.getItemCount () - 1) .getBottom ()
humblerookie
@Saren Arterius, możesz rzucić okiem na to
Sheraz Ahmad Khilji

Odpowiedzi:

109

Rozwiązanie znajduje się w menedżerze układu.

LinearLayoutManager layoutManager = new LinearLayoutManager(this);

// Add this to your Recycler view
recyclerView.setLayoutManager(layoutManager);

// To check if at the top of recycler view
if(layoutManager.firstCompletelyVisibleItemPosition()==0){
    // Its at top
}

// To check if at the bottom of recycler view
if(layoutManager.lastCompletelyVisibleItemPosition()==data.size()-1){
    // Its at bottom
}

EDYTOWAĆ

Jeśli rozmiar twojego przedmiotu jest większy niż ekran, użyj poniższych, aby wykryć największe zdarzenie.

RecyclerView recyclerView = (RecyclerView) view;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

int pos = linearLayoutManager.findFirstVisibleItemPosition();

if(linearLayoutManager.findViewByPosition(pos).getTop()==0 && pos==0){
    return true;
}

PS: Właściwie, jeśli umieścisz RecyclerViewbezpośrednio w środku SwipeRefreshview, nie musisz tego robić

humblerookie
źródło
Nie, nadal występuje, gdy RecyclerView jest bezpośrednim elementem podrzędnym SwipeRefreshLayout. Ale sprawdzenie firstCompletelyVisibleItemPosition rozwiązuje problem. Muszę tu coś wskazać. Jeśli żaden z elementów nie jest w pełni widoczny, metoda firstCompletelyVisibleItemPosition () zwraca -1. Więc to rozwiązanie nie zadziała, jeśli pierwszy element nie pasuje do RecyclerView, gdy jest przewijany do góry. Ale to bardzo rzadka sytuacja.
eluleci
1
@eluleci: Sprawdź, czy lista jest na górze RecyclerView rc = (RecyclerView) widok; LinearLayoutManager lm = (LinearLayoutManager) rc.getLayoutManager (); if (lm.findViewByPosition (lm.findFirstVisibleItemPosition ()). getTop () == 0 && lm.findFirstVisibleItemPosition () == 0) return true; // To zadziała, nawet jeśli element ma większy rozmiar niż ekran
humblerookie Kwietnia
2
To się nie powiedzie, jeśli element (pierwszy lub ostatni) jest większy niż cały ekran. Ponieważ, first/lastCompletelyVisibleItemPosition()zwróci -1, oznacza, że ​​nie ma w pełni widocznej pozycji.
Subin Sebastian,
4
Obie lastVisibleItemPositioni lastCompletelyVisibleItemPositionnie działają. (nie można rozwiązać metody). Wsparcie?
Stardust,
4
findLastCompletelyVisibleItemPositionnie powiedzie się, jeśli wysokość ostatniego elementu jest większa niż wysokość ekranu. Oznacza to, że ostatni element jest widoczny, ale nie do końca widoczny, więc layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION). lastVisibleItemPositionBezpieczniejsze będzie używanie i sprawdzanie dolnej części ostatniego widoku w układzie.
odszedł
78

Możesz spróbować recyclerView.canScrollVertically(int direction), jeśli chcesz tylko wiedzieć, czy można przewijać, czy nie.

Liczby całkowite kierunku:

  • -1 do góry
  • 1 w dół
  • 0 zawsze zwróci fałsz.
Penzzz
źródło
Trzeba by to jednak powtarzać co jakiś czas, prawda?
Stardust,
1
Możesz to zrobić w
recyclingView.addOnScrollListener
2
Świetny! To zwraca fałsz, gdy dojdziemy do końca przewijania, potrzebowałem tego zdarzenia.
pudełko
1
Świetny. Użyj tego w onScrollStateChangedpracy dla mnie.
Lym Zoy
5
Kierunek liczby całkowite: -1 dla góry, 1 dla dołu, 0 zawsze zwróci fałsz.
Grux
33

Aby sprawdzić, czy RecyclerViewjest przewijany do dołu. Użyj poniższego kodu.

/**
     * Check whether the last item in RecyclerView is being displayed or not
     *
     * @param recyclerView which you would like to check
     * @return true if last position was Visible and false Otherwise
     */
    private boolean isLastItemDisplaying(RecyclerView recyclerView) {
        if (recyclerView.getAdapter().getItemCount() != 0) {
            int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
            if (lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1)
                return true;
        }
        return false;
    }

Kilka dodatkowych informacji

Jeśli chcesz wprowadzić ScrollToBottomw RecyclerViewgdy Edittextjest tappedto polecam dodać 1 sekundowe opóźnienie tak:

 edittext.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP)
                    if (isLastItemDisplaying(recyclerView)) {
// The scrolling can happen instantly before keyboard even opens up so to handle that we add 1 second delay to scrolling
                        recyclerView.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount() - 1);

                            }
                        }, 1000);
                    }
                return false;
            }
        });
Sheraz Ahmad Khilji
źródło
Pracował dla mnie. Musiałem dodać go do RecyclerView za addOnScrollListener () choć
aphoe
1
nie działa to w trywialnym przypadku, gdy w widoku recyklingu znajduje się jeden element i jest on w pełni widoczny.
techtinkerer
uratowałeś dzień
Reena,
22

@Saren Arterius, używając addOnScrollListener z RecycleView , możesz znaleźć przewijaną górę lub dół Vertical RecycleView, jak poniżej,

RecyclerView rv = (RecyclerView)findViewById(R.id.rv);

rv.addOnScrollListener(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) {

                if (dy < 0) {
                    // Recycle view scrolling up...

                } else if (dy > 0) {
                    // Recycle view scrolling down...
                }
            }
        });
TejaDroid
źródło
6

Użyj recyclinglerView.canScrollVertically (int direction), aby sprawdzić, czy osiągnięto górną lub dolną część zwoju.

direction = 1 dla przewijania w dół (na dole)

direction = -1 dla przewijania w górę (do góry)

jeśli metoda zwraca false, oznacza to, że dotarłeś do góry lub do dołu, w zależności od kierunku.

kartik srivastava
źródło
Wielkie dzięki, że zadziałało!
Ferer Atlus
5

Po prostu zachowaj odniesienie do swojego layoutManagera i ustaw onScrollListener w widoku recyklera w ten sposób

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

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

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItemIndex = mLayoutManager.findFirstVisibleItemPosition();

        //synchronizew loading state when item count changes
        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading)
            if ((totalItemCount - visibleItemCount) <= firstVisibleItemIndex) {
                // Loading NOT in progress and end of list has been reached
                // also triggered if not enough items to fill the screen
                // if you start loading
                loading = true;
            } else if (firstVisibleItemIndex == 0){
                // top of list reached
                // if you start loading
                loading = true;
            }
        }
    }
});
SGal
źródło
1
setOnScrollListnerjest teraz przestarzała.
Shajeel Afzal
2
Powinieneś używać, addOnScrollListenerponieważ, jak powiedział @shajeelAfzal, setOnScrollListenerjest teraz przestarzałe, powinieneś używać, addOnScrollListenerponieważ, jak powiedział @shajeelAfzal, setOnScrollListenerjest teraz przestarzałe, sprawdź tutaj
Vasilisfoo
Co to jest previousTotal?
CoolMind
3

Możesz wykryć górę, używając następującego

 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

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

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if(IsRecyclerViewAtTop())
            {
                //your recycler view reached Top do some thing
             }
        }
    });

  private boolean IsRecyclerViewAtTop()   {
        if(recyclerView.getChildCount() == 0)
            return true;
        return recyclerView.getChildAt(0).getTop() == 0;
    }

Spowoduje to wykrycie góry, gdy zwolnisz palec po osiągnięciu górnej części, jeśli chcesz wykryć, gdy tylko ponownie przegląd osiągnie górę, sprawdź metodę if(IsRecyclerViewAtTop())wewnątrzonScrolled

Manohar Reddy
źródło
3
To wydaje się być błędne. getChildAt()zwraca pierwsze dziecko elementu ViewGroup, które może (i będzie) różnić się od pierwszego elementu listy. Jeśli zdarzy się, że to dziecko jest idealnie wyrównane z górną częścią widoku recyklingu, Twoja IsRecyclerViewAtTop()metoda zwróci wartość true, nawet jeśli nie znajduje się na górze.
aspyct
2

Napisałem RecyclerViewHelper, aby wiedzieć, że widok recyklingu jest na górze lub na dole.

public class RecyclerViewHelper {
public static boolean isAtTop(RecyclerView recyclerView) {
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        return isAtTopBeforeIceCream(recyclerView);
    } else {
        return !ViewCompat.canScrollVertically(recyclerView, -1);
    }
}

private static boolean isAtTopBeforeIceCream(RecyclerView recyclerView) {
    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    if (layoutManager instanceof LinearLayoutManager) {
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
        int pos = linearLayoutManager.findFirstVisibleItemPosition();
        if (linearLayoutManager.findViewByPosition(pos).getTop() == recyclerView.getPaddingTop() && pos == 0)
            return true;
    }
    return false;
}


public static boolean isAtBottom(RecyclerView recyclerView) {
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        return isAtBottomBeforeIceCream(recyclerView);
    } else {
        return !ViewCompat.canScrollVertically(recyclerView, 1);
    }
}

private static boolean isAtBottomBeforeIceCream(RecyclerView recyclerView) {
    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    int count = recyclerView.getAdapter().getItemCount();
    if (layoutManager instanceof LinearLayoutManager) {
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
        int pos = linearLayoutManager.findLastVisibleItemPosition();
        int lastChildBottom = linearLayoutManager.findViewByPosition(pos).getBottom();
        if (lastChildBottom == recyclerView.getHeight() - recyclerView.getPaddingBottom() && pos == count - 1)
            return true;
    }
    return false;
}

}

chefish
źródło
2

możesz to zrobić, to praca dla mnie

      mRecycleView.addOnScrollListener(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);
                int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ?
                        0 : recyclerView.getChildAt(0).getTop();
                LinearLayoutManager linearLayoutManager1 = (LinearLayoutManager) recyclerView.getLayoutManager();
                int firstVisibleItem = linearLayoutManager1.findFirstVisibleItemPosition();
                swipeRefreshLayout.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
            }
        });
Amal Kronz
źródło
2

Aby dowiedzieć się, czy widok RecyclerView jest przewijany do dołu, możesz ponownie użyć metod używanych dla paska przewijania.

Oto obliczenia dla wygody zapisane jako funkcja rozszerzenia Kotlina:

fun RecyclerView.isScrolledToBottom(): Boolean {
    val contentHeight = height - (paddingTop + paddingBottom)
    return computeVerticalScrollRange() == computeVerticalScrollOffset() + contentHeight
}
Giso Bartels
źródło
Niezłe rozwiązanie! Widziałem pewne problemy, w których rozmiar przewijania był tylko 1 wyłączony, mimo że przewijano mnie na dole (prawdopodobnie niektóre problemy z zaokrągleniem), więc zmieniłem kod, aby użyć marginesu:fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight >= margin }
Renso Lohuis,
Mam na myśli mniejszą lub równą marży. W ten sposób:fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight <= margin }
Renso Lohuis
1

możesz spróbować z OnTouchListener:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        if (e.getAction() == MotionEvent.ACTION_UP
            || e.getAction() == MotionEvent.ACTION_MOVE){
        if (mLinearLayoutManager.findFirstCompletelyVisibleItemPosition() > 0)
        {
        // beginning of the recycler 
        }

        if (mLinearLayoutManager.findLastCompletelyVisibleItemPosition()+1 < recyclerView.getAdapter().getItemCount())
        {
        // end of the recycler 
        }         
     }             
return false;
}
Jch Pal
źródło