Wie implementiere ich endlose Listen mit RecyclerView?

346

Ich möchte ändern ListViewzu RecyclerView. Ich möchte das onScrollvon OnScrollListenerin verwenden, RecyclerViewum festzustellen, ob ein Benutzer zum Ende der Liste gescrollt hat.

Woher weiß ich, ob ein Benutzer zum Ende der Liste blättert, um neue Daten von einem REST-Service abzurufen?

erdna
quelle
Bitte überprüfen Sie diesen Link Es wird Ihnen helfen .. stackoverflow.com/questions/26293461/…
samsad
32
Bitte lesen Sie die Frage sorgfältig durch! Ich weiß, wie man das mit einer ListView macht, aber NICHT, wie man es mit einer RecycleView implementiert .
erdna
Wie man loadmore onscrolllistener in android.data implementiert, wird geladen, aber auf vorhandene Daten aktualisiert. Bitte helfen Sie mir
Harsha
2
Sie können diese Bibliothek verwenden: github.com/rockerhieu/rv-adapter-endless . Es basiert auf der Idee von cwac-endlessfür ListView.
Hieu Rocker

Antworten:

422

Danke an @Kushal und so habe ich es umgesetzt

private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (dy > 0) { //check for scroll down
            visibleItemCount = mLayoutManager.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();

            if (loading) {
                if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
                    loading = false;
                    Log.v("...", "Last Item Wow !");
                    // Do pagination.. i.e. fetch new data
                }
            }
        }
    }
});

Vergiss nicht hinzuzufügen

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
Abdulaziz Noor
quelle
11
Was kann ich tun fürStaggeredGridLayoutManager
Pratik Butani
16
Was ist mitmLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
Laaptu
45
Für alle, die findFirstVisibleItemPosition()nicht gelöst haben, sollten Sie RecyclerView.LayoutManagerzuLinearLayoutManager
Amr Mohammed
26
Wo ist die Voraussetzung, um loading = truewieder zu machen?
Bhargav
10
Vergessen Sie nicht, loading = truenach dem //Do paginationTeil hinzuzufügen . Andernfalls wird dieser Code nur einmal ausgeführt und Sie können kein weiteres Element laden.
Shaishav Jogani
165

Machen Sie diese Variablen.

private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;

Für die Recycler-Ansicht auf Scrollen setzen.

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();
        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount) 
            <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached

            Log.i("Yaeye!", "end called");

            // Do something

            loading = true;
        }
    }
});

Hinweis: Stellen Sie sicher, dass Sie LinearLayoutManagerals Layout-Manager für verwenden RecyclerView.

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);

und für ein Gitter

GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);

Viel Spaß mit deinen endlosen Schriftrollen !! ^. ^

Update: mRecyclerView. setOnScrollListener () ist veraltet. Ersetzen Sie es einfach durch mRecyclerView.addOnScrollListener()und die Warnung ist verschwunden! Sie können mehr von dieser SO-Frage lesen .

Da Android Kotlin jetzt offiziell unterstützt, gibt es hier ein Update dafür -

Machen Sie OnScrollListener

class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : RecyclerView.OnScrollListener() {
    var previousTotal = 0
    var loading = true
    val visibleThreshold = 10
    var firstVisibleItem = 0
    var visibleItemCount = 0
    var totalItemCount = 0

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        visibleItemCount = recyclerView.childCount
        totalItemCount = layoutManager.itemCount
        firstVisibleItem = layoutManager.findFirstVisibleItemPosition()

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false
                previousTotal = totalItemCount
            }
        }

        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            val initialSize = dataList.size
            updateDataList(dataList)
            val updatedSize = dataList.size
            recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) }
            loading = true
        }
    }
}

und fügen Sie es wie folgt zu Ihrem RecyclerView hinzu

recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))

Ein vollständiges Codebeispiel finden Sie in diesem Github-Repo .

Kushal Sharma
quelle
14
Haben Sie eine Idee, wie Sie diese Lösung mit einem GridLayoutManager anwenden können? Da die findFirstVisibleItemPosition()Methode nur mit dem LinearLayoutManager verfügbar ist, kann ich nicht herausfinden, wie sie funktioniert.
MathieuMaree
1
Zusätzliche Informationen: In diesem Fall visibleItemCount = mLayoutManager.getChildCount();verhält sich using genauso wie using visibleItemCount = mRecyclerView.getChildCount();.
Jing Li
3
Platzieren Sie den Code in der onScrollStateChanged () -Methode anstelle der onScrolled () -Methode.
Anirudh
2
@ toobsco42 Sie können dies verwenden:int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree
1
Was ist das findFirstVisibleItemPosition () nicht gelöst
Suresh Sharma
72

Für diejenigen, die nur benachrichtigt werden möchten, wenn der letzte Artikel vollständig angezeigt wird, können Sie verwenden View.canScrollVertically().

Hier ist meine Implementierung:

public abstract class OnVerticalScrollListener
        extends RecyclerView.OnScrollListener {

    @Override
    public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (!recyclerView.canScrollVertically(-1)) {
            onScrolledToTop();
        } else if (!recyclerView.canScrollVertically(1)) {
            onScrolledToBottom();
        } else if (dy < 0) {
            onScrolledUp();
        } else if (dy > 0) {
            onScrolledDown();
        }
    }

    public void onScrolledUp() {}

    public void onScrolledDown() {}

    public void onScrolledToTop() {}

    public void onScrolledToBottom() {}
}

Hinweis: Sie können verwenden, recyclerView.getLayoutManager().canScrollVertically()wenn Sie API <14 unterstützen möchten.

Hai Zhang
quelle
Dies ist eine großartige und einfache Antwort! Vielen Dank!
Andranik
2
Eine ähnliche Lösung kann zum Aktualisieren von Pulls verwendet werden, indem! RecyclerView.canScrollVertically (-1) überprüft wird.
LordParsley
Beste Lösung da draußen. Danke Alter!
Angmar
1
@ LordParsley Danke, aktualisiert. Dies ist auch eine der Methoden, mit denen SwipeRefreshLayout nach Pull zum Aktualisieren sucht.
Hai Zhang
1
@EpicPandaForce Wenn Sie verwenden, haben RecyclerViewSie wieder Glück, denn Sie können einfach eine statische Kopie davon erstellen, View.canScrollVertically()da die zugehörigen Methoden als öffentlich und nicht als geschützt markiert sind RecyclerView. Sie können die Erweiterung auch erweitern RecyclerView, canScrollVertically()wenn Sie möchten. Eine dritte und einfache Möglichkeit ist das Anrufen recyclerView.getLayoutManager().canScrollVertically(). In meiner Antwort bearbeitet.
Hai Zhang
67

Hier ist ein anderer Ansatz. Es funktioniert mit jedem Layout-Manager.

  1. Machen Sie die Adapterklasse abstrakt
  2. Erstellen Sie dann eine abstrakte Methode in der Adapterklasse (z. B. load ()).
  3. Überprüfen Sie in onBindViewHolder die Position, wenn zuletzt, und rufen Sie load () auf
  4. Überschreiben Sie die Funktion load (), während Sie das Adapterobjekt in Ihrer Aktivität oder Ihrem Fragment erstellen.
  5. Implementieren Sie in der Overided Load-Funktion Ihren loadmore-Aufruf

Für ein detailliertes Verständnis habe ich einen Blog-Beitrag und ein Beispielprojekt geschrieben. Hier finden Sie http://sab99r.com/blog/recyclerview-endless-load-more/

MyAdapter.java

public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            //check for last item
            if ((position >= getItemCount() - 1))
                load();
        }

        public abstract void load();
}

MyActivity.java

public class MainActivity extends AppCompatActivity {
    List<Items> items;
    MyAdapter adapter;

   @Override
    protected void onCreate(Bundle savedInstanceState) {
    ...
    adapter=new MyAdapter(items){
            @Override
            public void load() {
                //implement your load more here
                Item lastItem=items.get(items.size()-1);
                loadMore();
            }
        };
   }
}
Sabeer Mohammed
quelle
3
Wie bei der Lösung müssen Sie Ihren Adapter nicht abstrakt machen, sondern können eine Schnittstelle in Ihrem Adapter erstellen und von Ihrer Aktivität implementieren lassen, um sie flexibel und elegant zu halten, z. B.: ItemDisplayListener => onItemDisplayed (int position). Auf diese Weise können Sie an jeder Position laden: bei N, N-1, N-2 ..
Samer
1
@mcwise füge einen Booleschen Wert hinzu done = false; Wenn Sie das Ende erreicht haben, machen Sie fertig = wahr. Ändern Sie die aktuelle if-Bedingung in if ((Position> = getItemCount () - 1) && (! fertig))
Sabeer Mohammed
1
Dies sollte als die richtige Antwort markiert werden. Das ist elegant und einfach.
Greg Ennis
2
Ein gutes Beispiel für die überlegene Lösung, die unter der Popularität der ersten Lösung begraben liegt. Sehr oft wird die letzte Zeile an einen separaten Ansichtstyp gebunden, der als "Laden ..." - Banner dient. Wenn dieser Ansichtstyp einen Bindungsaufruf erhält, wird ein Rückruf eingerichtet, der eine Datenänderungsbenachrichtigung auslöst, wenn mehr Daten verfügbar sind. Die akzeptierte Antwort, den Scroll-Listener anzuschließen, ist verschwenderisch und funktioniert wahrscheinlich nicht für andere Layouttypen.
Michael Hays
3
@mcwise würde es nur endlos aufrufen, wenn der Benutzer aktiv zum letzten Element zurückblättert. onBindViewHolder wird nur einmal aufgerufen, wenn die Ansicht auf den Bildschirm gelangt, und würde sie nicht ständig aufrufen.
David Liu
18

Meine Antwort ist eine modifizierte Version von Noor. Ich ging von einem Ort, den ListViewich hatte EndlessScrollListener(den Sie in vielen Antworten auf SO leicht finden können) zu einem, RecyclerViewalso wollte EndlessRecyclScrollListenerich meinen früheren Hörer einfach aktualisieren.

Also hier ist der Code, hoffe es hilft:

public abstract class EndlessScrollRecyclListener extends RecyclerView.OnScrollListener
{
    // The total number of items in the dataset after the last load
    private int previousTotalItemCount = 0;
    private boolean loading = true;
    private int visibleThreshold = 5;
    int firstVisibleItem, visibleItemCount, totalItemCount;
    private int startingPageIndex = 0;
    private int currentPage = 0;

    @Override
    public void onScrolled(RecyclerView mRecyclerView, int dx, int dy)
    {
        super.onScrolled(mRecyclerView, dx, dy);
        LinearLayoutManager mLayoutManager = (LinearLayoutManager) mRecyclerView
                .getLayoutManager();

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
        onScroll(firstVisibleItem, visibleItemCount, totalItemCount);
    }

    public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount)
    {
        // If the total item count is zero and the previous isn't, assume the
        // list is invalidated and should be reset back to initial state
        if (totalItemCount < previousTotalItemCount)
        {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0)
            {
                this.loading = true;
            }
        }
        // If it’s still loading, we check to see if the dataset count has
        // changed, if so we conclude it has finished loading and update the current page
        // number and total item count.
        if (loading && (totalItemCount > previousTotalItemCount))
        {
            loading = false;
            previousTotalItemCount = totalItemCount;
            currentPage++;
        }

        // If it isn’t currently loading, we check to see if we have breached
        // the visibleThreshold and need to reload more data.
        // If we do need to reload some more data, we execute onLoadMore to fetch the data.
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem +
                visibleThreshold))
        {
            onLoadMore(currentPage + 1, totalItemCount);
            loading = true;
        }
    }

    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page, int totalItemsCount);

}
Federico Ponzi
quelle
1
Für Ihre Implementierung muss RecyclerView LinearLeayoutManager verwenden. Schlechte Idee!
Orri
@Orri Könnten Sie bitte erklären, warum? Vielen Dank!
Federico Ponzi
1
In Ihrem onScrolled haben Sie den LayoutManager von RecyclerView in LinearLayoutManager umgewandelt. Es funktioniert nicht für StaggredGridLayout oder irgendetwas anderes. In StaggredGridLayout ist findFirstVisibleItemPosition () nicht enthalten.
Orri
@Orri Danke, dass du darauf hingewiesen hast! Aber ich denke nicht, dass es eine so schlechte Idee ist, da es für mich und, basierend auf Upvotes, für andere Fälle funktioniert hat :)
Federico Ponzi
10

Für mich ist es sehr 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;
                // Scrolled to bottom. Do something here.
                mLoading = false;
            }
        }
    });

Seien Sie vorsichtig mit asynchronen Jobs: Das Laden von ml muss am Ende der asynchronen Jobs geändert werden. Hoffe es wird hilfreich sein!

Minh Nguyen
quelle
Ich fand diese Antwort nach Tagen, in denen ich mich selbst befragt hatte. Dies ist die beste Lösung, imo.
wsgeorge
Bitte geben Sie eine Lösung für Recyclerview mit loadmore Seitenzahl übergeben Android
Harsha
getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (neuer StoresAdapter.OnLoadMoreListener () {@Override public void onLoadMore () {// addiere null, sodass der Adapter view_type überprüft und den Fortschrittsbalken unten speichert () - 1); ++ pageNumber; getWebServiceData ();}}); beide rufen nacheinander endlich 2. Seite Daten nur sichtbar bitte helfen
Harsha
Ich habe viele Implementierungen der RecyclerView-Paginierung ausprobiert, aber keine davon hat funktioniert, außer dieser. Sie haben meine Gegenstimme
Mohammed Elrashied,
9

Die meisten Antwort geht davon aus dem RecyclerVieweinem verwendet LinearLayoutManager, oder GridLayoutManager, oder sogar StaggeredGridLayoutManager, oder unter der Annahme , dass das Scrollen vertikal oder horyzontal ist, aber niemand eine völlig generische Antwort geschrieben hat .

Die Verwendung des ViewHolderAdapters ist eindeutig keine gute Lösung. Ein Adapter kann mehr als 1 RecyclerViewverwenden. Es "passt" ihren Inhalt an. Es sollte die Recycler- Ansicht sein (dies ist die eine Klasse, die für das verantwortlich ist, was dem Benutzer aktuell angezeigt wird, und nicht der Adapter, der nur für die Bereitstellung von Inhalten für den Benutzer verantwortlich ist RecyclerView), die Ihr System darüber informieren muss, dass weitere Elemente benötigt werden (an Belastung).

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

/**
 * Listener to callback when the last item of the adpater is visible to the user.
 * It should then be the time to load more items.
 **/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {

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

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

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

        // check
        if (isLastItemVisible)
          onLastItemVisible(); // callback
     }
   }

   /**
    * Here you should load more items because user is seeing the last item of the list.
    * Advice: you should add a bollean value to the class
    * so that the method {@link #onLastItemVisible()} will be triggered only once
    * and not every time the user touch the screen ;)
    **/
   public abstract void onLastItemVisible();

}


// --- Exemple of use ---

myRecyclerView.setOnScrollListener(new LastItemListener() {
    public void onLastItemVisible() {
         // start to load more items here.
    }
}
Yairopro
quelle
1
Ihre Antwort ist richtig und funktioniert bis auf LayoutManagerseinen Moment: Verschieben Sie Ihren Berechnungscode in eine andere scrollListener-Methode - verschieben Sie ihn onScrollStateChangedstattdessen nach onScrolled. Und onScrollStateChangedüberprüfen Sie einfach:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer
Vielen Dank für Ihren Rat, aber ich denke nicht, dass es eine bessere Lösung wäre. Der Entwickler möchte, dass sein Listener ausgelöst wird, noch bevor die Recycling-Ansicht zum Scrollen stoppt. Das Laden neuer Datenelemente (wenn Daten lokal gespeichert sind) oder das Hinzufügen eines Fortschrittsbalken-Ansichtshalters am Ende der Recycling-Ansicht kann abgeschlossen sein. Wenn die Recycling-Ansicht zum Scrollen angehalten wird, kann es sich um einen Kreisfortschritt handeln -bar als letztes sichtbares Element.
Yairopro
6

Obwohl die akzeptierte Antwort einwandfrei funktioniert, verwendet die folgende Lösung addOnScrollListener, da setOnScrollListener veraltet ist und die Anzahl der Variablen sowie die if-Bedingungen reduziert.

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", "End of list");
                //loadMore();
            }
        }
    }
});
capt.swag
quelle
1
Manchmal wird meine loadMore () -Methode in onScrolled () dreimal aufgerufen. Jede Idee, warum sie dreimal aufgerufen wird und wie man aufhört, sie mehrmals aufzurufen.
ved
dy> 0 ist immer falsch
Dushyant Suthar
@ved Das liegt daran, dass das Scrollen drei Zustände hat: Scrollen, Scrollen, Leerlauf. Sie wählen eine, die geeignet ist.
TheRealChx101
5

Obwohl es so viele Antworten auf die Frage gibt, möchte ich unsere Erfahrungen mit der Erstellung der endlosen Listenansicht teilen. Wir haben kürzlich einen benutzerdefinierten Karussell-Layout-Manager implementiert, der im Zyklus funktionieren kann, indem die Liste unendlich und bis zu einem bestimmten Punkt gescrollt wird. Hier ist eine detaillierte Beschreibung auf GitHub .

Ich schlage vor, dass Sie sich diesen Artikel mit kurzen, aber wertvollen Empfehlungen zum Erstellen benutzerdefinierter LayoutManager ansehen: http://cases.azoft.com/create-custom-layoutmanager-android/

Vladimir Tchernitski
quelle
5

Mit der Leistung der Kotlin-Erweiterungsfunktionen kann der Code viel eleganter aussehen. Platzieren Sie dies an einer beliebigen Stelle (ich habe es in einer ExtensionFunctions.kt-Datei):

/**
 * WARNING: This assumes the layout manager is a LinearLayoutManager
 */
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:

youRecyclerView.addOnScrolledToEnd {
    //What you want to do once the end is reached
}

Diese Lösung basiert auf der Antwort von Kushal Sharma. Dies ist jedoch etwas besser, weil:

  1. Es verwendet onScrollStateChangedanstelle von onScroll. Dies ist besser, da onScrolles jedes Mal onScrollStateChangedaufgerufen wird, wenn sich in der RecyclerView irgendeine Bewegung befindet, während es nur aufgerufen wird, wenn der Status der RecyclerView geändert wird. Durch onScrollStateChangeddie Verwendung sparen Sie CPU-Zeit und damit Batterie.
  2. Da hier Erweiterungsfunktionen verwendet werden, kann dies in jeder RecyclerView verwendet werden, die Sie haben. Der Client-Code besteht nur aus 1 Zeile.
Tiago
quelle
Als ich die SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working private innere Klasse PullToRefresh gezogen habe: SwipeRefreshLayout.OnRefreshListener {Spaß überschreiben onRefresh () {pbViewMore !! ! .isRefreshing = false}}
Naveen Kumar M
5

So mache ich es, einfach und kurz:

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
    {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            if(!recyclerView.canScrollVertically(1) && dy != 0)
            {
                // Load more results here

            }
        }
    });
John T.
quelle
3

OK, ich habe es mit der onBindViewHolder- Methode von RecyclerView.Adapter gemacht.

Adapter:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    ...

    if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem();
}

Fragment (oder Aktivität) :

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    contentView = inflater.inflate(R.layout.comments_list, container, false);
    recyclerView = (RecyclerView) mContentView.findViewById(R.id.my_recycler_view);
    adapter = new Adapter();
    recyclerView.setAdapter(adapter);

    ...

    adapter.setOnViewHolderListener(new Adapter.OnViewHolderListener() {
        @Override
        public void onRequestedLastItem() {
            //TODO fetch new data from webservice
        }
    });
    return contentView;
}
erdna
quelle
1
sich auf onBind zu verlassen ist keine sehr gute Idee. RecyclerView führt Cache-Ansichten durch + hat einige Layout-Manager, die Mittel zum Vor-Cache von Dingen haben. Ich würde vorschlagen, einen Bildlauf-Listener festzulegen. Wenn der Bildlauf-Listener stoppt, erhalten Sie die letzte sichtbare Elementposition vom Layout-Manager. Wenn Sie Standard-Layout-Manager verwenden, verfügen alle über findLastVisible ** -Methoden.
Yigit
@yigit Wie weit entfernt wird RecyclerView versuchen, Dinge vorzuspeichern? Wenn Sie 500 Elemente haben, bezweifle ich wirklich, dass es so weit entfernt zwischengespeichert wird (andernfalls ist es nur eine riesige Leistungsverschwendung). Wenn es tatsächlich zu cachen beginnt (selbst wenn wir bei 480 element sind), bedeutet dies, dass es sowieso Zeit ist, einen weiteren Stapel zu laden.
Alex Orlov
1
Es macht Snot-PreCache-Dinge, es sei denn, es ist ein reibungsloses Scrollen zu einer weit entfernten Position. In diesem Fall legt LLM (standardmäßig) zwei Seiten anstelle von einer Seite an, damit die Zielansicht gefunden und abgebremst werden kann. Es werden Cache-Ansichten erstellt, die außerhalb der Grenzen liegen. Standardmäßig beträgt diese Cache-Größe 2 und kann über setItemCacheSize konfiguriert werden.
Yigit
1
@yigit Ich denke nur über das Problem nach. Ich sehe nicht, wie das Caching hier ein Problem darstellen kann. wir interessieren uns nur für das erste mal, wenn die "letzten" artikel sowieso gebunden sind! Das Fehlen des Aufrufs von onBindViewHolder, wenn der Layout-Manager eine bereits gebundene zwischengespeicherte Ansicht wiederverwendet, ist kein Problem, wenn wir daran interessiert sind, am Ende der Liste weitere Daten einzugeben. Richtig?
Greg Ennis
2
In meiner FlexibleAdapter-Bibliothek habe ich mich entschieden, die endlose Schriftrolle beim Aufrufen zu übernehmen onBindViewHolder(). Sie funktioniert wie ein Zauber. Die Implementierung ist im Vergleich zum Scroll-Listener winzig und einfacher zu bedienen.
Davideas
3

Meine Art, ein Ladeereignis zu erkennen, besteht nicht darin, einen Bildlauf zu erkennen, sondern zu überwachen, ob die letzte Ansicht angehängt wurde. Wenn die letzte Ansicht angehängt wurde, betrachte ich sie als Zeitpunkt, um mehr Inhalte zu laden.

class MyListener implements RecyclerView.OnChildAttachStateChangeListener {
    RecyclerView mRecyclerView;

    MyListener(RecyclerView view) {
        mRecyclerView = view;
    }

    @Override
    public void onChildViewAttachedToWindow(View view) {

    RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
    RecyclerView.LayoutManager mgr = mRecyclerView.getLayoutManager();
    int adapterPosition = mgr.getPosition(view);

    if (adapterPosition == adapter.getItemCount() - 1) {
        // last view was attached
        loadMoreContent();
    }

    @Override
    public void onChildViewDetachedFromWindow(View view) {}
}
Walkingice
quelle
2

Ich würde versuchen, die verwendete LayoutManager(z. B. LinearLayoutManager) und überschreibende scrollVerticallyBy()Methode zu erweitern. Zuerst würde ich superzuerst aufrufen und dann den zurückgegebenen ganzzahligen Wert überprüfen. Wenn der Wert gleich ist, 0wird ein unterer oder ein oberer Rand erreicht. Dann würde ich die findLastVisibleItemPosition()Methode verwenden, um herauszufinden, welche Grenze erreicht ist, und bei Bedarf weitere Daten laden. Nur eine Idee.

Darüber hinaus können Sie sogar Ihren Wert von dieser Methode zurückgeben, indem Sie ein Überschreiben und das Anzeigen der Anzeige "Laden" zulassen.

sergej shafarenka
quelle
2
 recyclerList.setOnScrollListener(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) 
                {
                    int totalItemCount = layoutManager.getItemCount();
                    int lastVisibleItem = layoutManager.findLastVisibleItemPosition();

                    if (totalItemCount> 1) 
                    {
                        if (lastVisibleItem >= totalItemCount - 1) 
                        {
                            // End has been reached
                            // do something 
                        }
                    }          
                }
            });  
Anirudh
quelle
1
onScrollStateChanged()funktioniert nicht, da es nur aufgerufen wird, wenn Sie den Status des Bildlaufs ändern und nicht, während Sie scrollen. Sie müssen die onScrolled()Methode verwenden.
mradzinski
2

Mit dieser Logik in der onBindViewHolderMethode meiner RecyclerView.AdapterKlasse habe ich eine Implementierung vom Typ "Unendlicher Bildlauf" erreicht .

    if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) {
        if (mCurrentPage == mTotalPageCount) {
            mLoadImagesListener.noMorePages();
        } else {
            int newPage = mCurrentPage + 1;
            mLoadImagesListener.loadPage(newPage);
        }
    }

Wenn der RecyclerView mit diesem Code zum letzten Element gelangt, werden die aktuelle Seite und die Rückrufe auf einer Schnittstelle erhöht, die für das Laden weiterer Daten aus der API und das Hinzufügen der neuen Ergebnisse zum Adapter verantwortlich ist.

Ich kann ein vollständigeres Beispiel posten, wenn dies nicht klar ist.

Speedynomaden
quelle
Ich weiß, dass dieser Beitrag wirklich alt ist, aber ich bin ein bisschen ein Neuling und weiß nicht, wo ich die Variablen bekommen soll, die Sie vergleichen. mCurrentPage und mTotalPageCount beziehen sich vermutlich auf Ihren Datensatz, und mItems ist die Arrayliste der Listenelemente, aber wie finde ich die Position?
BooleanCheese
Sie können hier sehen, wie ich meinen RecyclerView.Adapter implementiert habe: github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/…
speedynomads
2

Für Leute, die StaggeredGridLayoutManager verwenden , ist hier meine Implementierung, es funktioniert für mich.

 private class ScrollListener extends RecyclerView.OnScrollListener {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

        firstVivisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVivisibleItems);

        if(!recyclerView.canScrollVertically(1) && firstVivisibleItems[0]!=0) {
            loadMoreImages();
        }

    }

    private boolean loadMoreImages(){
        Log.d("myTag", "LAST-------HERE------");
        return true;
    }
}
Dennis Zinkovski
quelle
@ SudheeshMohan Hier ist volle Klasse, es ist klein) In meinem Projekt ändere ich dynamisch die Anzahl der Spannen und es wird für 1 und 2 Spannen funktionieren
Dennis Zinkovski
recyclerView.canScrollVertically (1) wird es tun, aber es erfordert API 14. :(
Sudheesh Mohan
2

Es gibt eine einfach zu bedienende Bibliothek für diesen Namen Paginate . Unterstützt sowohl ListView als auch RecyclerView (LinearLayout, GridLayout und StaggeredGridLayout).

Hier ist der Link zum Projekt auf Github

nonybrighto
quelle
2
  1. Erstellen Sie eine abstrakte Klasse und erweitern Sie RecyclerView.OnScrollListener

    public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
    private int previousTotal = 0;
    private boolean loading = true;
    private int visibleThreshold;
    private int firstVisibleItem, visibleItemCount, totalItemCount;
    private RecyclerView.LayoutManager layoutManager;
    
    public EndlessRecyclerOnScrollListener(RecyclerView.LayoutManager layoutManager, int visibleThreshold) {
    this.layoutManager = layoutManager; this.visibleThreshold = visibleThreshold;
    }
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    
    visibleItemCount = recyclerView.getChildCount();
    totalItemCount = layoutManager.getItemCount();
    firstVisibleItem = ((LinearLayoutManager)layoutManager).findFirstVisibleItemPosition();
    
    if (loading) {
        if (totalItemCount > previousTotal) {
            loading = false;
            previousTotal = totalItemCount;
        }
    }
    if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
        onLoadMore();
        loading = true;
    }
      }
    
    public abstract void onLoadMore();}
  2. Fügen Sie in Aktivität (oder Fragment) addOnScrollListener zu recyclerView hinzu

    LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager, 3) {
        @Override
        public void onLoadMore() {
            //TODO
            ...
        }
    });
roghayeh hosseini
quelle
1

Ich habe ein ziemlich detailliertes Beispiel für die Paginierung mit einem RecyclerView. Auf einer hohen Ebene habe ich eine Menge PAGE_SIZE, sagen wir 30. Also fordere ich 30 Artikel an und wenn ich 30 zurück bekomme, fordere ich die nächste Seite an. Wenn ich weniger als 30 Elemente erhalte, markiere ich eine Variable, um anzuzeigen, dass die letzte Seite erreicht wurde, und fordere dann keine weiteren Seiten mehr an. Probieren Sie es aus und lassen Sie mich wissen, was Sie denken.

https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b

toobsco42
quelle
1

Hier lautet meine Lösung mit AsyncListUtil im Web: Beachten Sie, dass diese Klasse zum Laden der Daten einen einzelnen Thread verwendet, sodass Daten vom sekundären Speicher wie der Festplatte, jedoch nicht vom Netzwerk geladen werden können. Aber ich benutze Odata, um die Daten zu lesen und gut zu funktionieren. Ich vermisse in meinem Beispiel Datenentitäten und Netzwerkmethoden. Ich füge nur den Beispieladapter hinzu.

public class AsyncPlatoAdapter extends RecyclerView.Adapter {

    private final AsyncPlatoListUtil mAsyncListUtil;
    private final MainActivity mActivity;
    private final RecyclerView mRecyclerView;
    private final String mFilter;
    private final String mOrderby;
    private final String mExpand;

    public AsyncPlatoAdapter(String filter, String orderby, String expand, RecyclerView recyclerView, MainActivity activity) {
        mFilter = filter;
        mOrderby = orderby;
        mExpand = expand;
        mRecyclerView = recyclerView;
        mActivity = activity;
        mAsyncListUtil = new AsyncPlatoListUtil();

    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).
                inflate(R.layout.plato_cardview, parent, false);

        // Create a ViewHolder to find and hold these view references, and
        // register OnClick with the view holder:
        return new PlatoViewHolderAsync(itemView, this);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final Plato item = mAsyncListUtil.getItem(position);
        PlatoViewHolderAsync vh = (PlatoViewHolderAsync) holder;
        if (item != null) {
            Integer imagen_id = item.Imagen_Id.get();
            vh.getBinding().setVariable(BR.plato, item);
            vh.getBinding().executePendingBindings();
            vh.getImage().setVisibility(View.VISIBLE);
            vh.getProgress().setVisibility(View.GONE);
            String cacheName = null;
            String urlString = null;
            if (imagen_id != null) {
                cacheName = String.format("imagenes/imagen/%d", imagen_id);
                urlString = String.format("%s/menusapi/%s", MainActivity.ROOTPATH, cacheName);
            }
            ImageHelper.downloadBitmap(mActivity, vh.getImage(), vh.getProgress(), urlString, cacheName, position);
        } else {
            vh.getBinding().setVariable(BR.plato, item);
            vh.getBinding().executePendingBindings();
            //show progress while loading.
            vh.getImage().setVisibility(View.GONE);
            vh.getProgress().setVisibility(View.VISIBLE);
        }
    }

    @Override
    public int getItemCount() {
        return mAsyncListUtil.getItemCount();
    }

    public class AsyncPlatoListUtil extends AsyncListUtil<Plato> {
        /**
         * Creates an AsyncListUtil.
         */
        public AsyncPlatoListUtil() {
            super(Plato.class, //my data class
                    10, //page size
                    new DataCallback<Plato>() {
                        @Override
                        public int refreshData() {
                            //get count calling ../$count ... odata endpoint
                            return countPlatos(mFilter, mOrderby, mExpand, mActivity);
                        }

                        @Override
                        public void fillData(Plato[] data, int startPosition, int itemCount) {
                            //get items from odata endpoint using $skip and $top
                            Platos p = loadPlatos(mFilter, mOrderby, mExpand, startPosition, itemCount, mActivity);
                            for (int i = 0; i < Math.min(itemCount, p.value.size()); i++) {
                                data[i] = p.value.get(i);
                            }

                        }
                    }, new ViewCallback() {
                        @Override
                        public void getItemRangeInto(int[] outRange) {
                            //i use LinearLayoutManager in the RecyclerView
                            LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
                            outRange[0] = layoutManager.findFirstVisibleItemPosition();
                            outRange[1] = layoutManager.findLastVisibleItemPosition();
                        }

                        @Override
                        public void onDataRefresh() {
                            mRecyclerView.getAdapter().notifyDataSetChanged();
                        }

                        @Override
                        public void onItemLoaded(int position) {
                            mRecyclerView.getAdapter().notifyItemChanged(position);
                        }
                    });
            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    onRangeChanged();
                }
            });
        }
    }
}
FRL
quelle
1

@kushal @abdulaziz

Warum nicht stattdessen diese Logik verwenden?

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    int totalItemCount, lastVisibleItemPosition;

    if (dy > 0) {
      totalItemCount = _layoutManager.getItemCount();
      lastVisibleItemPosition = _layoutManager.findLastVisibleItemPosition();

      if (!_isLastItem) {
        if ((totalItemCount - 1) == lastVisibleItemPosition) {
          LogUtil.e("end_of_list");

          _isLastItem = true;
        }
      }
    }
  }
leonapse
quelle
Mein onScroller () hat dreimal für ein einzelnes Element aufgerufen, sodass ich dreimal end_of_list und meine Paginierungslogik dreimal aufgerufen bekomme. So korrigieren Sie dies, um die Paginierung nur einmal aufzurufen.
ved
@ved Das soll nicht passieren. Dieser obige Code stellt sicher, dass die end_of_listBedingung nur einmal aufgerufen wird. Das Flag _isLastItemist eine globale Variable in der Aktivität mit dem Standardwert false. Ich habe eine Hintergrundaufgabe ausgeführt, nachdem ich das Ende der Liste erkannt, die nächste Seite abgerufen, den Adapter über das neue Dataset benachrichtigt und schließlich wieder _isLastItemauf false gesetzt habe.
Leonapse
1

Versuchen Sie es unten:

import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;

/**
 * Abstract Endless ScrollListener
 * 
 */
public abstract class EndlessScrollListener extends
        RecyclerView.OnScrollListener {
    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 10;
    // The current offset index of data you have loaded
    private int currentPage = 1;
    // True if we are still waiting for the last set of data to load.
    private boolean loading = true;
    // The total number of items in the data set after the last load
    private int previousTotal = 0;
    private int firstVisibleItem;
    private int visibleItemCount;
    private int totalItemCount;
    private LayoutManager layoutManager;

    public EndlessScrollListener(LayoutManager layoutManager) {
        validateLayoutManager(layoutManager);
        this.layoutManager = layoutManager;
    }

    public EndlessScrollListener(int visibleThreshold,
            LayoutManager layoutManager, int startPage) {
        validateLayoutManager(layoutManager);
        this.visibleThreshold = visibleThreshold;
        this.layoutManager = layoutManager;
        this.currentPage = startPage;
    }

    private void validateLayoutManager(LayoutManager layoutManager)
            throws IllegalArgumentException {
        if (null == layoutManager
                || !(layoutManager instanceof GridLayoutManager)
                || !(layoutManager instanceof LinearLayoutManager)) {
            throw new IllegalArgumentException(
                    "LayoutManager must be of type GridLayoutManager or LinearLayoutManager.");
        }
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = layoutManager.getItemCount();
        if (layoutManager instanceof GridLayoutManager) {
            firstVisibleItem = ((GridLayoutManager) layoutManager)
                    .findFirstVisibleItemPosition();
        } else if (layoutManager instanceof LinearLayoutManager) {
            firstVisibleItem = ((LinearLayoutManager) layoutManager)
                    .findFirstVisibleItemPosition();
        }
        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading
                && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached do something
            currentPage++;
            onLoadMore(currentPage);
            loading = true;
        }
    }

    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page);

}
Shridutt Kothari
quelle
1

Ich habe LoadMoreRecyclerView mit Abdulaziz Noor Answer erstellt

LoadMoreRecyclerView

public class LoadMoreRecyclerView extends RecyclerView  {

    private boolean loading = true;
    int pastVisiblesItems, visibleItemCount, totalItemCount;
    //WrapperLinearLayout is for handling crash in RecyclerView
    private WrapperLinearLayout mLayoutManager;
    private Context mContext;
    private OnLoadMoreListener onLoadMoreListener;

    public LoadMoreRecyclerView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        init();
    }

    private void init(){
        mLayoutManager = new WrapperLinearLayout(mContext,LinearLayoutManager.VERTICAL,false);
        this.setLayoutManager(mLayoutManager);
        this.setItemAnimator(new DefaultItemAnimator());
        this.setHasFixedSize(true);
    }

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

        if(dy > 0) //check for scroll down
        {
            visibleItemCount = mLayoutManager.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();

            if (loading)
            {
                if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount)
                {
                    loading = false;
                    Log.v("...", "Call Load More !");
                    if(onLoadMoreListener != null){
                        onLoadMoreListener.onLoadMore();
                    }
                    //Do pagination.. i.e. fetch new data
                }
            }
        }
    }

    @Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
    }

    public void onLoadMoreCompleted(){
        loading = true;
    }

    public void setMoreLoading(boolean moreLoading){
        loading = moreLoading;
    }

    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        this.onLoadMoreListener = onLoadMoreListener;
    }
}

WrapperLinearLayout

public class WrapperLinearLayout extends LinearLayoutManager
{
    public WrapperLinearLayout(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("probe", "meet a IOOBE in RecyclerView");
        }
    }
}

// Füge es in XML wie hinzu

<your.package.LoadMoreRecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</your.package.LoadMoreRecyclerView>

OnCreate oder onViewCreated

mLoadMoreRecyclerView = (LoadMoreRecyclerView) view.findViewById(R.id.recycler_view);
mLoadMoreRecyclerView.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                callYourService(StartIndex);
            }
        });

Rufen Sie Ihren Service an

private void callYourService(){
    //callyour Service and get response in any List

    List<AnyModelClass> newDataFromServer = getDataFromServerService();
    //Enable Load More
    mLoadMoreRecyclerView.onLoadMoreCompleted();

    if(newDataFromServer != null && newDataFromServer.size() > 0){
            StartIndex += newDataFromServer.size();

            if (newDataFromServer.size() < Integer.valueOf(MAX_ROWS)) {
                    //StopLoading..
                   mLoadMoreRecyclerView.setMoreLoading(false);
            }
    }
    else{
            mLoadMoreRecyclerView.setMoreLoading(false);
            mAdapter.notifyDataSetChanged();
    }
}
Zar E Ahmer
quelle
1

Es ist auch möglich, ohne den Scroll-Listener zu implementieren, wobei nur die reine Logik des Datenmodells verwendet wird. Die Bildlaufansicht erfordert das Abrufen von Elementen nach Position sowie die maximale Anzahl von Elementen. Das Modell kann über die Hintergrundlogik verfügen, um die erforderlichen Elemente nicht einzeln, sondern in Blöcken abzurufen, und dies im Hintergrundthread, um die Ansicht zu benachrichtigen, wenn die Daten bereit sind.

Dieser Ansatz ermöglicht es, dass die Abrufwarteschlange, die zuletzt angeforderte (also derzeit sichtbare) Elemente gegenüber älteren (höchstwahrscheinlich bereits weggescrollten) Übermittlungen bevorzugt, die Anzahl der zu verwendenden parallelen Threads und dergleichen steuert. Den vollständigen Quellcode für diesen Ansatz (Demo-App und wiederverwendbare Bibliothek) finden Sie hier .

Audrius Meskauskas
quelle
0

Es gibt eine Methode public void setOnScrollListener (RecyclerView.OnScrollListener listener)in https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29 . Verwende das

BEARBEITEN:

Überschreiben Sie die onScrollStateChangedMethode innerhalb von onScrollListenerund tun Sie dies

            boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount;

            //loading is used to see if its already loading, you have to manually manipulate this boolean variable
            if (loadMore && !loading) {
                 //end of list reached
            }
Panther
quelle
1
Bitte sei spezifischer! Wie verwende ich RecyclerView.OnScrollListener, um festzustellen, ob der Benutzer zum Ende der Liste gescrollt hat?
Erdna
Bei RecyclerView.OnScrollListener gibt es kein onScroll ! Sie mischen den AbsListView.OnScrollListener mit dem RecyclerView.OnScrollListener.
erdna
ya es soll seinonScrollStateChanged
Panther
onScrollStateChangedfunktioniert nicht, da es nur aufgerufen wird, wenn Sie den Status des Bildlaufs ändern und nicht, während Sie scrollen.
Pedro Oliveira
@PedroOliveira Kann mit onScrollStateChangedfestgestellt werden, dass der Benutzer zum letzten Element scrollt?
Erdna
0

Überprüfen Sie, ob alles im Detail erklärt wird: Paginierung mit RecyclerView Von A bis Z.

    mRecyclerView.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 visibleItemCount = mLayoutManager.getChildCount();
        int totalItemCount = mLayoutManager.getItemCount();
        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();

        if (!mIsLoading && !mIsLastPage) {
            if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
                    && firstVisibleItemPosition >= 0) {
                loadMoreItems();
            }
        }
    }
})

loadMoreItems ():

private void loadMoreItems() {
    mAdapter.removeLoading();
    //load data here from the server

    // in case of success
    mAdapter.addData(data);
    // if there might be more data
    mAdapter.addLoading();
}

in MyAdapter:

private boolean mIsLoadingFooterAdded = false;

public void addLoading() {
    if (!mIsLoadingFooterAdded) {
        mIsLoadingFooterAdded = true;
        mLineItemList.add(new LineItem());
        notifyItemInserted(mLineItemList.size() - 1);
    }
}

public void removeLoading() {
    if (mIsLoadingFooterAdded) {
        mIsLoadingFooterAdded = false;
        int position = mLineItemList.size() - 1;
        LineItem item = mLineItemList.get(position);

        if (item != null) {
            mLineItemList.remove(position);
            notifyItemRemoved(position);
        }
    }
}

public void addData(List<YourDataClass> data) {

    for (int i = 0; i < data.size(); i++) {
        YourDataClass yourDataObject = data.get(i);
        mLineItemList.add(new LineItem(yourDataObject));
        notifyItemInserted(mLineItemList.size() - 1);
    }
}
awsleiman
quelle
Der Link enthält möglicherweise eine Antwort, aber versuchen Sie, einige wesentliche Teile der Erklärungen im Link in Ihrem Beitrag zu erläutern.
Ian
0

Keine dieser Antworten berücksichtigt, ob die Liste zu klein ist oder nicht.

Hier ist ein Code, den ich verwendet habe und der in RecycleViews in beide Richtungen funktioniert.

@Override
    public boolean onTouchEvent(MotionEvent motionEvent) {

        if (recyclerViewListener == null) {
            return super.onTouchEvent(motionEvent);
        }

        /**
         * If the list is too small to scroll.
         */
        if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
            if (!canScrollVertically(1)) {
                recyclerViewListener.reachedBottom();
            } else if (!canScrollVertically(-1)) {
                recyclerViewListener.reachedTop();
            }
        }

        return super.onTouchEvent(motionEvent);
    }

    public void setListener(RecyclerViewListener recycleViewListener) {
        this.recyclerViewListener = recycleViewListener;
        addOnScrollListener(new OnScrollListener() {

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

                if (recyclerViewListener == null) {
                    return;
                }

                recyclerViewListener.scrolling(dy);

                if (!canScrollVertically(1)) {
                    recyclerViewListener.reachedBottom();
                } else if (!canScrollVertically(-1)) {
                    recyclerViewListener.reachedTop();
                }
            }

        });
    }
Oliver Dixon
quelle
0

Ich lasse dich meine Annäherung. Funktioniert gut für mich.

Ich hoffe es hilft dir.

/**
 * Created by Daniel Pardo Ligorred on 03/03/2016.
 */
public abstract class BaseScrollListener extends RecyclerView.OnScrollListener {

    protected RecyclerView.LayoutManager layoutManager;

    public BaseScrollListener(RecyclerView.LayoutManager layoutManager) {

        this.layoutManager = layoutManager;

        this.init();
    }

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

        super.onScrolled(recyclerView, dx, dy);

        this.onScroll(recyclerView, this.getFirstVisibleItem(), this.layoutManager.getChildCount(), this.layoutManager.getItemCount(), dx, dy);
    }

    private int getFirstVisibleItem(){

        if(this.layoutManager instanceof LinearLayoutManager){

            return ((LinearLayoutManager) this.layoutManager).findFirstVisibleItemPosition();
        } else if (this.layoutManager instanceof StaggeredGridLayoutManager){

            int[] spanPositions = null; //Should be null -> StaggeredGridLayoutManager.findFirstVisibleItemPositions makes the work.

            try{

                return ((StaggeredGridLayoutManager) this.layoutManager).findFirstVisibleItemPositions(spanPositions)[0];
            }catch (Exception ex){

                // Do stuff...
            }
        }

        return 0;
    }

    public abstract void init();

    protected abstract void onScroll(RecyclerView recyclerView, int firstVisibleItem, int visibleItemCount, int totalItemCount, int dx, int dy);

}
Dani
quelle
0

Diese Lösung funktioniert perfekt für mich.

//Listener    

public abstract class InfiniteScrollListener     extendsRecyclerView.OnScrollListener {

public static String TAG = InfiniteScrollListener.class.getSimpleName();
int firstVisibleItem, visibleItemCount, totalItemCount;
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 1;
private int current_page = 1;

private LinearLayoutManager mLinearLayoutManager;

public InfiniteScrollListener(LinearLayoutManager linearLayoutManager) {
    this.mLinearLayoutManager = linearLayoutManager;
}

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

    visibleItemCount = recyclerView.getChildCount();
    totalItemCount = mLinearLayoutManager.getItemCount();
    firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();

    if (loading) {
        if (totalItemCount > previousTotal) {
            loading = false;
            previousTotal = totalItemCount;
        }
    }
    if (!loading && (totalItemCount - visibleItemCount - firstVisibleItem <= visibleThreshold)) {

        current_page++;

        onLoadMore(current_page);

        loading = true;
    }
}

public void resetState() {
    loading = true;
    previousTotal = 0;
    current_page = 1;
}

public abstract void onLoadMore(int current_page);
}

//Implementation into fragment 
 private InfiniteScrollListener scrollListener;

scrollListener = new InfiniteScrollListener(manager) {
        @Override
        public void onLoadMore(int current_page) {
            //Load data
        }
    };
    rv.setLayoutManager(manager);
    rv.addOnScrollListener(scrollListener);
Giedrius Šlikas
quelle