Come implementare un elenco infinito con RecyclerView?


346

Vorrei cambiare ListViewa RecyclerView. Voglio usare il onScrolldi OnScrollListenerin RecyclerViewper determinare se un utente scorre fino alla fine dell'elenco.

Come faccio a sapere se un utente scorre fino alla fine dell'elenco in modo da poter recuperare nuovi dati da un servizio REST?


Si prega di controllare questo link Essa vi aiuterà a .. stackoverflow.com/questions/26293461/...
Samsad

32
Si prega di leggere attentamente la domanda! So come farlo con un ListView ma NON come implementarlo con un RecycleView .
Erdna,

come implementare loadmore onscrolllistener in android.data è caricato ma aggiornato su dati esistenti per favore aiutatemi
Harsha,

2
È possibile utilizzare questa libreria: github.com/rockerhieu/rv-adapter-endless . Si basa sull'idea di cwac-endlessper ListView.
Hieu Rocker,

Risposte:


422

Grazie a @Kushal ed è così che l'ho implementato

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

Non dimenticare di aggiungere

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

11
Cosa posso fare perStaggeredGridLayoutManager
Pratik Butani

16
Che diremLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
laaptu

46
A tutti coloro che findFirstVisibleItemPosition()non si sono risolti, dovresti passare RecyclerView.LayoutManageraLinearLayoutManager
Amr Mohammed,

27
dov'è la condizione per rifare loading = true?
Bhargav,

11
Non dimenticare di aggiungere loading = truedopo la //Do paginationparte. altrimenti questo codice verrà eseguito una sola volta e non sarà possibile caricare più articoli.
Shaishav Jogani,

165

Crea queste variabili.

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

Impostare su Scorri per visualizzare il riciclatore.

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

Nota: assicurati di utilizzare LinearLayoutManagercome gestore del layout per RecyclerView.

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

e per una griglia

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

Divertiti con le tue pergamene infinite !! ^. ^

Aggiornamento: mRecyclerView. setOnScrollListener () è deprecato, basta sostituirlo con mRecyclerView.addOnScrollListener()e l'avvertimento sparirà ! Puoi leggere di più da questa domanda SO .

Poiché Android ora supporta ufficialmente Kotlin, ecco un aggiornamento per lo stesso:

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

e aggiungilo al tuo RecyclerView in questo modo

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

Per un esempio di codice completo, fai riferimento a questo repository Github .


14
Qualche idea su come applicare questa soluzione con un GridLayoutManager? Poiché il findFirstVisibleItemPosition()metodo è disponibile solo con LinearLayoutManager, non riesco a capire come farlo funzionare.
MathieuMaree,

1
Informazioni aggiuntive: in questo caso, l'utilizzo visibleItemCount = mLayoutManager.getChildCount();si comporta come se fosse un utilizzo visibleItemCount = mRecyclerView.getChildCount();.
Jing Li,

3
Inserire il codice nel metodo onScrollStateChanged () anziché nel metodo onScrolled ().
Anirudh,

2
@ toobsco42 Puoi usare questo:int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree,

1
Che cos'è questo findFirstVisibleItemPosition () non risolto
Suresh Sharma

72

Per coloro che desiderano ricevere una notifica solo quando l'ultimo elemento viene mostrato completamente, è possibile utilizzare View.canScrollVertically().

Ecco la mia implementazione:

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() {}
}

Nota: è possibile utilizzare recyclerView.getLayoutManager().canScrollVertically()se si desidera supportare l'API <14.


Questa è una risposta grande e semplice! Grazie!
Andranik,

2
Una soluzione simile può essere utilizzata per pull to refresh controllando! RecyclerView.canScrollVertically (-1).
LordParsley,

La migliore soluzione là fuori. Grazie amico!
Angmar,

1
@LordParsley Grazie, aggiornato. Questo è anche uno dei modi in cui SwipeRefreshLayout controlla pull per aggiornare.
Hai Zhang,

1
@EpicPandaForce Se stai usando RecyclerViewsarai di nuovo fortunato, perché puoi semplicemente fare una copia statica View.canScrollVertically()poiché i metodi correlati sono contrassegnati come pubblici anziché protetti RecyclerView. Se lo desideri, puoi anche estenderlo RecyclerViewper implementarlo nuovamente canScrollVertically(). Un terzo e semplice modo è chiamare recyclerView.getLayoutManager().canScrollVertically(). Modificato nella mia risposta.
Hai Zhang,

67

Ecco un altro approccio. Funzionerà con qualsiasi gestore di layout.

  1. Rendi la classe dell'adattatore astratta
  2. Quindi creare un metodo astratto in classe adattatore (ad es. Load ())
  3. In onBindViewHolder controlla la posizione se ultimo e chiama load ()
  4. Sostituisci la funzione load () durante la creazione dell'oggetto adattatore nella tua attività o frammento.
  5. Nella funzione di caricamento overided implementare la chiamata loadmore

Per una comprensione dettagliata ho scritto un post sul blog e un esempio di progetto, scaricalo qui 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();
            }
        };
   }
}

3
Come la soluzione, non è necessario rendere astratto l'adattatore, ma è possibile creare un'interfaccia nell'adattatore e rendere l'implementazione della tua attività mantenendola flessibile ed elegante, ad esempio: ItemDisplayListener => onItemDisplayed (int position). In questo modo puoi caricare in qualsiasi posizione: alle N, N-1, N-2 ..
Samer,

1
@mcwise aggiungi un booleano finito = falso; quando raggiungi la fine make finito = true .. cambia la condizione if attuale in if ((position> = getItemCount () - 1) && (! finito))
Sabeer Mohammed

1
Questo dovrebbe essere contrassegnato come la risposta corretta. Questo è elegante e semplice.
Greg Ennis,

2
Un buon esempio della soluzione superiore sepolta sotto la popolarità della prima soluzione. Molto spesso, l'ultima riga verrà associata a un tipo di vista separato che funge da banner "Caricamento in corso ...". Quando questo tipo di vista riceve una chiamata di bind, imposta una richiamata che attiva una notifica di modifica dei dati quando sono disponibili più dati. La risposta accettata di collegare l'ascoltatore di scorrimento è inutile e probabilmente non funziona con altri tipi di layout.
Michael Hays,

3
@mcwise chiamerebbe all'infinito solo se l'utente scorre attivamente via e torna all'ultimo elemento. onBindViewHolder viene chiamato solo una volta ogni volta che la vista entra nello schermo e non viene chiamata costantemente.
David Liu,

18

La mia risposta è una versione modificata di Noor. Sono passato da un luogo in ListViewcui avevo EndlessScrollListener(che puoi trovare facilmente in molte risposte su SO) a un RecyclerViewcosì ho voluto EndlessRecyclScrollListeneraggiornare facilmente il mio ascoltatore passato.

Quindi ecco il codice, spero che aiuti:

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

}

1
L'implementazione richiede che RecyclerView utilizzi LinearLeayoutManager. Cattiva idea!
Orri,

@Orri Potresti spiegare perché? Grazie!
Federico Ponzi,

1
Nel tuo onScrolled hai lanciato LayoutManager da RecyclerView a LinearLayoutManager. Non funziona per StaggredGridLayout o altro. Non c'è findFirstVisibleItemPosition () in StaggredGridLayout.
Orri,

@Orri Grazie per averlo sottolineato! Ma non penso che sia una cattiva idea, dal momento che ha funzionato con il mio e, in base alle valutazioni, altri casi :)
Federico Ponzi,

10

Per me è molto semplice:

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

Prestare attenzione con i lavori asincroni: mLoading deve essere modificato alla fine dei lavori asincroni. Spero possa essere utile!


Ho trovato questa risposta dopo giorni in cui mi sono interrogato, praticamente. Questa è la soluzione migliore, imo.
mercoledì

per favore, dai una soluzione per il riciclaggio con il numero di pagina loadmore che passa android
Harsha,

getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (new StoresAdapter.OnLoadMoreListener () {@Override public void onLoadMore () {// aggiungi null, quindi l'adattatore controllerà view_type e mostrerà la barra di avanzamento nei negozi inferioriList.add (null); mStoresLapdata.Itemdistore (store) () - 1); ++ pageNumber; getWebServiceData ();}}); entrambi si chiamano uno dopo l'altro, alla fine i dati della 2a pagina sono visibili solo per favore
Harsha,

ho provato molte implementazioni della paginazione di recyclerView ma nessuna di esse ha funzionato, tranne questa, hai il mio signore di
voto

9

La maggior parte delle risposte presuppone che gli RecyclerViewusi a LinearLayoutManager, o GridLayoutManager, o anche StaggeredGridLayoutManager, o che lo scorrimento sia verticale o orizzontale, ma nessuno ha pubblicato una risposta completamente generica .

L'uso ViewHolderdell'adattatore non è chiaramente una buona soluzione. Un adattatore potrebbe utilizzarne più di 1 RecyclerView. "Adatta" il loro contenuto. Dovrebbe essere la vista Riciclatore (che è l'unica classe responsabile di ciò che è attualmente visualizzato all'utente e non l'adattatore che è responsabile solo di fornire contenuti al RecyclerView) che deve informare il sistema che sono necessari più articoli (a caricare).

Ecco la mia soluzione, usando nient'altro che le classi astratte di RecyclerView (RecycerView.LayoutManager e RecycerView.Adapter):

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

1
La tua risposta è giusta ed ha funzionato per tutti, LayoutManagersma solo per un momento: sposta il tuo codice di calcolo in un altro metodo scrollListener - spostalo onScrollStateChangedinvece onScrolled. E onScrollStateChangedbasta controllare:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer

Grazie per il tuo consiglio, ma non credo che sarebbe una soluzione migliore. Lo sviluppatore desidera che il suo listener venga attivato anche prima che la riciclerview interrompa lo scorrimento. Quindi potrebbe finire per caricare nuovi elementi di dati (se i dati sono memorizzati localmente) o addirittura aggiungere un supporto per la barra di avanzamento alla fine della vista di riciclo, quindi quando la vista di riciclo smetterà di scorrere, può essere in corso un cerchio -bar come ultimo elemento visibile.
Yairopro,

6

Sebbene la risposta accettata funzioni perfettamente, la soluzione seguente utilizza addOnScrollListener poiché setOnScrollListener è obsoleto e riduce il numero di variabili e se le condizioni.

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

1
A volte il mio metodo loadMore () chiamato tre volte in onScrolled (), qualsiasi idea del perché si chiama tre volte e come smettere di chiamarlo più volte.
visto il

dy> 0 è sempre falso
Dushyant Suthar,

@ved Questo perché lo scorrimento ha 3 stati: scorrimento, scorrimento, inattivo. Ne scegli uno adatto.
TheRealChx101,

5

Sebbene ci siano così tante risposte alla domanda, vorrei condividere la nostra esperienza di creazione della vista elenco infinita. Di recente abbiamo implementato il layout manager personalizzato del carosello che può funzionare nel ciclo facendo scorrere l'elenco all'infinito e fino a un certo punto. Ecco una descrizione dettagliata su GitHub .

Ti suggerisco di dare un'occhiata a questo articolo con brevi ma preziosi consigli sulla creazione di LayoutManager personalizzati: http://cases.azoft.com/create-custom-layoutmanager-android/


5

Con la potenza delle funzioni di estensione di Kotlin, il codice può apparire molto più elegante. Metti questo dove vuoi (ce l'ho dentro un file ExtensionFunctions.kt):

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

E poi usalo in questo modo:

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

Questa soluzione si basa sulla risposta di Kushal Sharma. Tuttavia, questo è un po 'meglio perché:

  1. Usa onScrollStateChangedinvece di onScroll. Questo è meglio perché onScrollviene chiamato ogni volta che c'è un qualche tipo di movimento in RecyclerView, mentre onScrollStateChangedviene chiamato solo quando lo stato di RecyclerView viene modificato. L'uso onScrollStateChangedconsente di risparmiare tempo di CPU e, di conseguenza, batteria.
  2. Poiché utilizza le funzioni di estensione, può essere utilizzato in qualsiasi RecyclerView. Il codice client è solo 1 riga.

Quando l'ho fatto SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working , PullToRefresh: classe interna privata PullToRefresh: SwipeRefreshLayout.OnRefreshListener {ignora divertimento suRefresh () {pbViewMore !!. ! .isRefreshing = false}}
Naveen Kumar M

5

Ecco come lo faccio, semplice e breve:

    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

            }
        }
    });

3

OK, l'ho fatto usando il metodo onBindViewHolder di RecyclerView.Adapter.

Adattatore:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

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

    ...

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

Frammento (o attività) :

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

1
fare affidamento su onBind non è una buona idea. RecyclerView memorizza nella cache + ha alcuni gestori di layout che hanno i mezzi per pre-cache delle cose. Vorrei suggerire di impostare un listener di scorrimento e quando il listener di scorrimento si interrompe, ottenere l'ultima posizione dell'elemento visibile dal gestore del layout. Se si utilizzano i gestori layout predefiniti, hanno tutti i metodi findLastVisible **.
yigit

@yigit Fino a che punto RecyclerView proverà a precache le cose? Se hai 500 articoli, dubito davvero che verrà memorizzato nella cache così lontano (altrimenti sarà solo un enorme spreco di prestazioni). Quando inizierà effettivamente a memorizzare nella cache (anche quando abbiamo 480 elementi) ciò significa che è tempo di caricare comunque un altro batch.
Alex Orlov,

1
Effettua lo snot delle cose preCache a meno che non stia scorrendo lentamente verso una posizione lontana. In tal caso, LLM (per impostazione predefinita) esegue il layout di due pagine anziché di una in modo da poter trovare la vista di destinazione e rallentarla. Esegue viste cache che vanno oltre i limiti. Per impostazione predefinita, questa dimensione della cache è 2 e può essere configurata tramite setItemCacheSize.
yigit,

1
@yigit sto solo pensando al problema che non vedo come la cache potrebbe presentare un problema qui; siamo interessati solo alla prima volta che gli "ultimi" articoli vengono comunque associati! Perdere la chiamata a onBindViewHolder quando il gestore del layout riutilizza una vista memorizzata nella cache che era già associata non è un problema quando siamo interessanti nel paging di più dati alla fine dell'elenco. Corretta?
Greg Ennis,

2
Nella mia libreria FlexibleAdapter ho scelto di adottare lo scroll infinito con la chiamata onBindViewHolder(), funziona come un fascino. L'implementazione è minuscola rispetto al listener di scorrimento ed è più facile da usare.
Davideas,

3

Il mio modo di rilevare l'evento di caricamento non è rilevare lo scorrimento, ma ascoltare se l'ultima vista è stata allegata. Se è stata allegata l'ultima vista, ritengo che sia un tempismo per caricare più contenuti.

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

2

Vorrei provare a estendere il metodo usato LayoutManager(es. LinearLayoutManager) E override scrollVerticallyBy(). In primo luogo, vorrei chiamare superprima e quindi controllare il valore intero restituito. Se il valore è uguale a 0allora viene raggiunto un bordo inferiore o superiore. Quindi userei il findLastVisibleItemPosition()metodo per scoprire quale confine viene raggiunto e caricare più dati se necessario. Solo un'idea

Inoltre, puoi anche restituire il tuo valore da quel metodo, consentendo l'overscroll e mostrando l'indicatore "caricamento".


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

1
onScrollStateChanged()non funzionerà poiché verrà chiamato solo quando cambi lo stato dello scorrimento e non mentre stai scorrendo. È necessario utilizzare il onScrolled()metodo
mradzinski,

2

Ho ottenuto un'implementazione del tipo a scorrimento infinito usando questa logica nel onBindViewHoldermetodo della mia RecyclerView.Adapterclasse.

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

Con questo codice quando RecyclerView arriva all'ultimo elemento, incrementa la pagina corrente e le richiamate su un'interfaccia che è responsabile del caricamento di più dati dall'API e dell'aggiunta dei nuovi risultati all'adattatore.

Posso pubblicare un esempio più completo se questo non è chiaro?


So che questo post è davvero vecchio, ma sono in qualche modo un newb e non so dove trovare le variabili che stai confrontando. mCurrentPage e mTotalPageCount si riferiscono al tuo set di dati presumo, e mItems è l'arraylist degli elementi dell'elenco, ma come trovo la posizione?
BooleanCheese

Puoi vedere come ho implementato il mio RecyclerView.Adapter qui: github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/…
speedynomads

2

Per le persone che usano StaggeredGridLayoutManager ecco la mia implementazione, funziona per me.

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

@SudheeshMohan Ecco la classe completa, è piccola) Nel mio progetto cambio dinamicamente il conteggio delle
campate

recyclerView.canScrollVertically (1) lo farà ma richiede API 14. :(
Sudheesh Mohan

2

C'è una libreria facile da usare per questo nome impaginato . Supporta sia ListView che RecyclerView (LinearLayout, GridLayout e StaggeredGridLayout).

Ecco il link al progetto su Github


2
  1. Crea una classe astratta ed estende 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. in attività (o frammento) aggiungi addOnScrollListener a recyclerView

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

1

Ho un esempio abbastanza dettagliato di come impaginare con un RecyclerView. Ad un livello elevato, ho impostato PAGE_SIZE, diciamo 30. Quindi richiedo 30 articoli e se ne ottengo 30, richiedo la pagina successiva. Se ricevo meno di 30 elementi, contrassegno una variabile per indicare che è stata raggiunta l'ultima pagina e quindi smetto di richiedere più pagine. Dai un'occhiata e fammi sapere cosa ne pensi.

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


1

Qui la mia soluzione che utilizza AsyncListUtil , nel Web, dice: Nota che questa classe utilizza un singolo thread per caricare i dati, quindi è adatta a caricare i dati dall'archiviazione secondaria come il disco, ma non dalla rete. ma sto usando Odata per leggere i dati e lavorare bene. Mi mancano nel mio esempio entità dati e metodi di rete. Includo solo l'adattatore di esempio.

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

1

@kushal @abdulaziz

Perché non usare questa logica invece?

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

Il mio onScroller () ha chiamato tre volte per un singolo elemento, quindi ottengo tre volte end_of_list e la mia logica di impaginazione ha chiamato tre volte. Come correggerlo per chiamare l'impaginazione una sola volta.
visto il

@ved Non dovrebbe succedere. Tale codice sopra assicura che la end_of_listcondizione verrà chiamata una sola volta. Il flag _isLastItemè una variabile globale nell'attività che ha un valore predefinito di false. Quello che ho fatto è stato eseguire un'attività in background dopo aver rilevato la fine dell'elenco, recuperando la pagina successiva, quindi notificare all'adattatore il nuovo set di dati e infine impostare _isLastItemnuovamente su false.
leonapse,

1

Prova di seguito:

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

}

1

Ho creato LoadMoreRecyclerView usando Abdulaziz Noor Answer

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

// Aggiungilo come xml

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

OnCreate o onViewCreated

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

callYourService

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

1

È anche possibile implementare senza il listener di scorrimento, utilizzando solo la logica pura del modello di dati. La vista di scorrimento richiede di ottenere gli articoli in base alla posizione e il conteggio massimo degli articoli. Il modello può avere la logica di background per recuperare gli elementi necessari in blocchi, anziché uno per uno, e farlo nel thread in background, notificando la vista quando i dati sono pronti.

Questo approccio consente di avere la coda di recupero che preferisce gli elementi richiesti più di recente (quindi attualmente visibili) rispetto agli invii più vecchi (molto probabilmente già scartati), controllare il numero di thread paralleli da usare e cose simili. Il codice sorgente completo per questo approccio (app demo e libreria riutilizzabile) è disponibile qui .


0

Esiste un metodo 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 . Usa quello

MODIFICARE:

Sostituisci onScrollStateChangedmetodo all'interno di onScrollListenere fai questo

            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
            }

1
Sii più specifico! Come posso usare RecyclerView.OnScrollListener per determinare che l'utente ha fatto scorrere fino alla fine dell'elenco?
Erdna,

Non c'è onScroll su RecyclerView.OnScrollListener ! Mescoli AbsListView.OnScrollListener con RecyclerView.OnScrollListener.
Erdna,

si suppone che siaonScrollStateChanged
Pantera,

onScrollStateChangednon funzionerà poiché verrà chiamato solo quando cambi lo stato dello scorrimento e non mentre stai scorrendo.
Pedro Oliveira,

@PedroOliveira È possibile onScrollStateChangeddeterminare se l'utente scorre fino all'ultimo elemento?
Erdna,

0

Dai un'occhiata a tutto ciò che è spiegato in dettaglio: Impaginazione usando RecyclerView dalla A alla 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);
    }
}

Il link può contenere una risposta, ma prova a spiegare alcune parti sostanziali delle spiegazioni nel link nel tuo post.
Ian

0

Nessuna di queste risposte tiene conto se l'elenco è troppo piccolo o meno.

Ecco un pezzo di codice che sto usando che funziona su RecycleViews in entrambe le direzioni.

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

        });
    }

0

Ti ho lasciato la mia approssimazione. Funziona bene per me.

Spero che ti aiuti.

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

}

0

Questa soluzione funziona perfettamente per me.

//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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.