Woher wissen Sie, ob ein RecyclerView / LinearLayoutManager nach oben oder unten gescrollt wird?


80

Derzeit verwende ich den folgenden Code, um zu überprüfen, ob SwipeRefreshLayout aktiviert werden soll.

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

Aber hier ist das Problem. Wenn es zur Ansichtsgrenze eines anderen Elements gescrollt wird, wird mRecyclerView.getChildAt(0).getTop()ebenfalls 0 zurückgegeben.

Das Problem

Gibt es so etwas wie RecyclerView.isScrolledToBottom()oder RecyclerView.isScrolledToTop()?

EDIT: (mRecyclerView.getChildAt(0).getTop() == 0 && linearLayoutManager.findFirstVisibleItemPosition() == 0)Art macht das RecyclerView.isScrolledToTop(), aber was ist mit RecyclerView.isScrolledToBottom()?


Ich nehme an, dass Letzteres erreicht werden kann, indem der untere Teil der Recycling-Ansicht gegen das letzte untergeordnete Element überprüft wird, dh etwas in den Zeilen von mRecyclerView.getBottom () == linearLayoutmanager.findViewbyPosition (adapter.getItemCount () - 1) .getBottom ()
humblerookie

@ Saren Arterius Sie möchten vielleicht einen Blick darauf
werfen

Antworten:


108

Die Lösung befindet sich im Layout-Manager.

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
}

BEARBEITEN

Wenn Ihre Artikelgröße größer als der Bildschirm ist, verwenden Sie Folgendes, um das Hauptereignis zu erkennen.

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

int pos = linearLayoutManager.findFirstVisibleItemPosition();

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

PS: Wenn Sie das RecyclerViewdirekt in das platzieren, müssen SwipeRefreshviewSie dies nicht tun


Nein, es passiert immer noch, wenn RecyclerView das direkte untergeordnete Element von SwipeRefreshLayout ist. Das Überprüfen der firstCompletelyVisibleItemPosition behebt jedoch das Problem. Ich muss hier etwas zeigen. Wenn keines der Elemente vollständig sichtbar ist, gibt firstCompletelyVisibleItemPosition () -1 zurück. Diese Lösung funktioniert also nicht, wenn das erste Element nicht in die RecyclerView passt, wenn es nach oben gescrollt wird. Aber es ist eine sehr seltene Situation.
Eluleci

1
@eluleci: Überprüfen Sie, ob die Liste oben angezeigt wird. RecyclerView rc = (RecyclerView) -Ansicht; LinearLayoutManager lm = (LinearLayoutManager) rc.getLayoutManager (); if (lm.findViewByPosition (lm.findFirstVisibleItemPosition ()). getTop () == 0 && lm.findFirstVisibleItemPosition () == 0) return true; // Dies würde evn funktionieren, wenn der Artikel größer als der Bildschirm ist
humblerookie

2
Dies würde fehlschlagen, wenn das Element (erstes oder letztes) größer als der gesamte Bildschirm ist. Da first/lastCompletelyVisibleItemPosition()-1 zurückgegeben wird, bedeutet dies, dass überhaupt kein vollständig sichtbares Element vorhanden ist.
Subin Sebastian

4
Beides lastVisibleItemPositionund lastCompletelyVisibleItemPositionfunktioniert nicht. (Methode kann nicht aufgelöst werden). Hilfe?
Stardust

4
findLastCompletelyVisibleItemPositionwird fehlschlagen, wenn die Höhe des letzten Elements größer als die Bildschirmhöhe ist. Dies bedeutet, dass das letzte Element sichtbar, aber nicht vollständig sichtbar ist layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION). Die Verwendung lastVisibleItemPositionund Überprüfung des unteren Randes der letzten Ansicht im Layout ist sicherer.
gegangen

78

Sie können versuchen recyclerView.canScrollVertically(int direction), ob Sie nur wissen müssen, ob ein Bildlauf möglich ist oder nicht.

Richtungszahlen:

  • -1 für bis
  • 1 für unten
  • 0 gibt immer false zurück.

Dies müsste allerdings in regelmäßigen Abständen wiederholt werden, oder?
Stardust

1
Sie können dies in recyclerView.addOnScrollListener .. es funktioniert wie ein Zauber ... danke
therealprashant

2
Großartig! Dies gibt eine falsche zurück, wenn wir das Ende des Bildlaufs erreichen. Ich brauchte dieses Ereignis.
Box

1
Ausgezeichnet. Verwenden Sie dies in onScrollStateChangedArbeiten für mich.
Lym Zoy

5
Richtungs-Ganzzahlen: -1 für Auf, 1 für Ab, 0 geben immer false zurück.
Grux

33

Um zu überprüfen, ob RecyclerViewnach unten gescrollt wird. Verwenden Sie den folgenden Code.

/**
     * 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;
    }

Einige zusätzliche Informationen

Wenn Sie implementieren möchten ScrollToBottomin RecyclerViewwann Edittextist tappeddann empfehle ich Ihnen hinzufügen 1 Sekunde Verzögerung wie folgt aus :

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

Hat für mich gearbeitet. Ich hatte es zu den wichtigsten RecyclerView hinzuzufügen addOnScrollListener () obwohl
aphoe

1
Dies funktioniert nicht für den trivialen Fall, in dem sich ein Element in der Recycling-Ansicht befindet und es vollständig sichtbar ist.
Techtinkerer

Sie haben einen Tag gerettet
Reena

22

@Saren Arterius, Mit dem addOnScrollListener von RecycleView können Sie den Bildlauf oben oder unten in Vertical RecycleView wie unten finden.

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

6

Verwenden Sie recyclerView.canScrollVertically (int-Richtung) , um zu überprüfen, ob der obere oder untere Rand des Bildlaufs erreicht ist.

Richtung = 1 für Bildlauf nach unten (unten)

Richtung = -1 für Bildlauf nach oben (oben)

Wenn die Methode false zurückgibt, bedeutet dies, dass Sie entweder oben oder unten erreicht haben. Dies hängt von der Richtung ab.


Vielen Dank, es hat funktioniert!
Ferer Atlus

5

Behalten Sie einfach einen Verweis auf Ihren layoutManager und setzen Sie onScrollListener in Ihrer Recycler-Ansicht wie folgt

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

1
setOnScrollListnerist jetzt veraltet.
Shajeel Afzal

2
Sie sollten verwenden, addOnScrollListenerweil, wie @shajeelAfzal sagte, setOnScrollListenerjetzt veraltet ist, sollten Sie verwenden, addOnScrollListenerweil, wie @shajeelAfzal sagte, setOnScrollListenerjetzt veraltet ist, überprüfen Sie hier
Vasilisfoo

Was ist previousTotal?
CoolMind

3

Sie können oben anhand der folgenden Informationen erkennen

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

Dies erkennt oben, wenn Sie den Finger loslassen, sobald er oben erreicht ist. Wenn Sie erkennen möchten, sobald die Reacyclerview die Top-Check- if(IsRecyclerViewAtTop())Inside- onScrolled Methode erreicht


3
Das scheint falsch zu sein. getChildAt()Gibt das erste untergeordnete ViewGroupElement von zurück, das sich vom ersten Element der Liste unterscheiden kann (und wird). Wenn dieses untergeordnete Element perfekt am oberen Rand der Recycling-Ansicht ausgerichtet ist, gibt Ihre IsRecyclerViewAtTop()Methode true zurück, auch wenn sie sich nicht oben befindet.
Aspyct

2

Ich habe einen RecyclerViewHelper geschrieben, um zu wissen, dass sich der Recyclerview oben oder unten befindet.

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

}}


2

Sie können dies tun, es ist Arbeit für mich

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

2

Um herauszufinden, ob die RecyclerView nach unten gescrollt ist, können Sie die für die Bildlaufleiste verwendeten Methoden wiederverwenden.

Dies ist die Berechnung, die der Einfachheit halber als Kotlin-Erweiterungsfunktion geschrieben wurde:

fun RecyclerView.isScrolledToBottom(): Boolean {
    val contentHeight = height - (paddingTop + paddingBottom)
    return computeVerticalScrollRange() == computeVerticalScrollOffset() + contentHeight
}

Schöne Lösung! Ich habe einige Probleme gesehen, bei denen die Bildlaufgröße nur 1 war, obwohl ich unten fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight >= margin }
gescrollt wurde

Ich meine, kleiner oder gleich der Marge. So:fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight <= margin }
Renso Lohuis

1

Sie können es mit dem OnTouchListener versuchen:

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;
}
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.