381 Stimmen

Wie implementiere ich eine endlose Liste mit RecyclerView?

Ich möchte ListView in RecyclerView ändern. Ich möchte das onScroll des OnScrollListener in RecyclerView verwenden, um festzustellen, ob ein Benutzer ans Ende der Liste gescrollt ist.

Wie kann ich feststellen, ob ein Benutzer ans Ende der Liste gescrollt ist, damit ich neue Daten von einem REST-Dienst abrufen kann?

14voto

Minh Nguyen Punkte 149

Für mich ist es ganz einfach:

     private boolean mLoading = false;

     mList.setOnScrollListener(new RecyclerView.OnScrollListener() {

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

            int totalItem = mLinearLayoutManager.getItemCount();
            int lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();

            if (!mLoading && lastVisibleItem == totalItem - 1) {
                mLoading = true;
                // Am Ende von asynchronen Aufgaben muss mLoading geändert werden. Hier etwas machen.
                mLoading = false;
            }
        }
    });

Seien Sie vorsichtig mit asynchronen Jobs: mLoading muss am Ende der asynchronen Jobs geändert werden. Hoffentlich wird es hilfreich sein!

11voto

Mit der Kraft der Erweiterungsfunktionen von Kotlin kann der Code viel eleganter aussehen. Platziere dies überall dort, wo du möchtest (ich habe es in einer Datei ExtensionFunctions.kt):

/**
 * WARNUNG: Dies setzt voraus, dass der Layout-Manager ein LinearLayoutManager ist
 */
fun RecyclerView.addOnScrolledToEnd(onScrolledToEnd: () -> Unit){

    this.addOnScrollListener(object: RecyclerView.OnScrollListener(){

        private val VISIBLE_THRESHOLD = 5

        private var loading = true
        private var previousTotal = 0

        override fun onScrollStateChanged(recyclerView: RecyclerView,
                                          newState: Int) {

            with(layoutManager as LinearLayoutManager){

                val visibleItemCount = childCount
                val totalItemCount = itemCount
                val firstVisibleItem = findFirstVisibleItemPosition()

                if (loading && totalItemCount > previousTotal){

                    loading = false
                    previousTotal = totalItemCount
                }

                if(!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)){

                    onScrolledToEnd()
                    loading = true
                }
            }
        }
    })
}

Und dann benutze es so:

deinRecyclerView.addOnScrolledToEnd {
    //Was du tun möchtest, wenn das Ende erreicht ist
}

Diese Lösung basiert auf Kushal Sharmas Antwort. Allerdings ist diese etwas besser, weil:

  1. Es verwendet onScrollStateChanged anstelle von onScroll. Das ist besser, weil onScroll jedes Mal aufgerufen wird, wenn es eine Art von Bewegung im RecyclerView gibt, während onScrollStateChanged nur aufgerufen wird, wenn der Zustand des RecyclerView geändert wird. Die Verwendung von onScrollStateChanged spart CPU-Zeit und damit Akkulaufzeit.
  2. Da dies Erweiterungsfunktionen verwendet, kann dies in jedem RecyclerView verwendet werden, den du hast. Der Client-Code ist nur eine Zeile.

9voto

Yairopro Punkte 6777

Die meisten Antworten gehen davon aus, dass der RecyclerView einen LinearLayoutManager, oder GridLayoutManager verwendet, oder sogar StaggeredGridLayoutManager, oder dass das Scrollen vertikal oder horizontal ist, aber niemand hat eine wirklich allgemeine Antwort gepostet.

Die Verwendung des ViewHolder-Adapters ist offensichtlich keine gute Lösung. Ein Adapter kann von mehr als 1 RecyclerView verwendet werden. Er "passt" ihren Inhalt an. Der RecyclerView (der die Klasse ist, die dafür verantwortlich ist, was dem Benutzer momentan angezeigt wird, und nicht der Adapter, der nur dafür verantwortlich ist, Inhalte für den RecyclerView bereitzustellen) sollte Ihr System benachrichtigen, dass mehr Elemente (zum Laden) benötigt werden.

Hier ist meine Lösung, die nichts anderes als die abstrahierten Klassen des RecyclerView verwendet (RecyclerView.LayoutManager und RecyclerView.Adapter):

/**
 * Listener, der zurückruft, wenn das letzte Element des Adapters für den Benutzer sichtbar ist.
 * Es sollte dann an der Zeit sein, mehr Elemente zu laden.
 **/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {

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

      // Initialisierung
      RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
      RecyclerView.Adapter adapter = recyclerView.getAdapter();

      if (layoutManager.getChildCount() > 0) {
        // Berechnungen
        int indexOfLastItemViewVisible = layoutManager.getChildCount() -1;
        View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible);
        int adapterPosition = layoutManager.getPosition(lastItemViewVisible);
        boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1);

        // Überprüfung
        if (isLastItemVisible)
          onLastItemVisible(); // Rückruf
     }
   }

   /**
    * Hier sollten Sie mehr Elemente laden, weil der Benutzer das letzte Element der Liste sieht.
    * Tipp: Sie sollten einen booleschen Wert zur Klasse hinzufügen,
    * damit die Methode {@link #onLastItemVisible()} nur einmal ausgelöst wird
    * und nicht jedes Mal, wenn der Benutzer den Bildschirm berührt ;)
    **/
   public abstract void onLastItemVisible();

}

// --- Beispiel für die Verwendung ---

myRecyclerView.setOnScrollListener(new LastItemListener() {
    public void onLastItemVisible() {
         // beginnen Sie hier mit dem Laden weiterer Elemente.
    }
}

8voto

John T Punkte 619

So mache ich das, einfach und kurz:

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
    {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            if(!recyclerView.canScrollVertically(1) && dy != 0)
            {
                // Hier weitere Ergebnisse laden

            }
        }
    });

7voto

capt.swag Punkte 9734

Obwohl die akzeptierte Antwort perfekt funktioniert, verwendet die unten stehende Lösung addOnScrollListener, da setOnScrollListener veraltet ist, und reduziert die Anzahl der Variablen und If-Bedingungen.

final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
feedsRecyclerView.setLayoutManager(layoutManager);

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

        if (dy > 0) {   
            if ((layoutManager.getChildCount() + layoutManager.findFirstVisibleItemPosition()) >= layoutManager.getItemCount()) {
                Log.d("TAG", "Ende der Liste");
                //Weitere laden();
            }
        }
    }
});

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X