Elenco infinito Android


232

Come posso creare un elenco in cui quando si raggiunge la fine dell'elenco sono stato avvisato in modo da poter caricare più elementi?


9
Raccomando EndlessAdapter di CommonWare: github.com/commonsguy/cwac-endless . L'ho trovato da questa risposta a un'altra domanda: stackoverflow.com/questions/4667064/… .
Tyler Collier,

Ho bisogno stesso thing..can qualsiasi aiuto ..? Stackoverflow.com/questions/28122304/...

Risposte:


269

Una soluzione consiste nell'implementare OnScrollListenere apportare modifiche (come aggiungere elementi, ecc.) ListAdapterA uno stato conveniente nel suo onScrollmetodo.

Di seguito viene ListActivitymostrato un elenco di numeri interi, che iniziano con 40, aggiungendo elementi quando l'utente scorre fino alla fine dell'elenco.

public class Test extends ListActivity implements OnScrollListener {

    Aleph0 adapter = new Aleph0();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setListAdapter(adapter); 
        getListView().setOnScrollListener(this);
    }

    public void onScroll(AbsListView view,
        int firstVisible, int visibleCount, int totalCount) {

        boolean loadMore = /* maybe add a padding */
            firstVisible + visibleCount >= totalCount;

        if(loadMore) {
            adapter.count += visibleCount; // or any other amount
            adapter.notifyDataSetChanged();
        }
    }

    public void onScrollStateChanged(AbsListView v, int s) { }    

    class Aleph0 extends BaseAdapter {
        int count = 40; /* starting amount */

        public int getCount() { return count; }
        public Object getItem(int pos) { return pos; }
        public long getItemId(int pos) { return pos; }

        public View getView(int pos, View v, ViewGroup p) {
                TextView view = new TextView(Test.this);
                view.setText("entry " + pos);
                return view;
        }
    }
}

Ovviamente dovresti usare thread separati per azioni di lunga durata (come il caricamento di dati web) e potresti voler indicare i progressi nell'ultimo elemento dell'elenco (come fanno le app di mercato o di Gmail).


3
eh, questo non farebbe aumentare la dimensione dell'adattatore se smettessi di scorrere al centro dello schermo? dovrebbe caricare solo i prossimi 10 quando raggiunge effettivamente la fine della lista.
Matthias,

9
Ho notato molti esempi come questo usando BaseAdapter. Volevo menzionare che se si utilizza un database, provare a utilizzare un SimpleCursorAdapter. Quando è necessario aggiungere all'elenco, aggiornare il database, ottenere un nuovo cursore e utilizzare SimpleCursorAdapter # changeCursor insieme a notificationDataSetChanged. Nessuna sottoclasse richiesta, e funziona meglio di un BaseAdapter (di nuovo, solo se stai usando un database).
parentesi

1
Dopo aver chiamato notifyDataSetChanged (), andrà in cima all'elenco, come posso impedirlo?
disegna il

2
Nota: ho usato questo approccio, ma avevo bisogno di aggiungere un controllo per quando visibleCount è 0. Per ogni elenco, ottengo un callback su onScroll dove firstVisible, visibleCount e totalCount sono tutti 0, che tecnicamente soddisfa il condizionale, ma non lo è quando voglio caricare di più.
Eric Mill,

5
Un altro problema con questo codice è che la condizione firstVisible + visibleCount >= totalCountè soddisfatta più volte con più chiamate all'ascoltatore. Se la funzione load-more è una richiesta Web, (molto probabilmente lo sarà), aggiungi un altro controllo per verificare se una richiesta è in corso o meno. D'altra parte, controlla se totalCount non è uguale al conteggio totale precedente, perché non abbiamo bisogno di più richieste per lo stesso numero di elementi nell'elenco.
Sottomarino Sebastian,

94

Volevo solo contribuire con una soluzione che ho usato per la mia app.

Si basa anche OnScrollListenersull'interfaccia, ma ho scoperto che ha prestazioni di scorrimento molto migliori sui dispositivi di fascia bassa, poiché nessuno dei calcoli del conteggio totale / visibile viene eseguito durante le operazioni di scorrimento.

  1. Lascia il tuo ListFragmento ListActivityimplementareOnScrollListener
  2. Aggiungi i seguenti metodi a quella classe:

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        //leave this empty
    }
    
    @Override
    public void onScrollStateChanged(AbsListView listView, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {
            if (listView.getLastVisiblePosition() >= listView.getCount() - 1 - threshold) {
                currentPage++;
                //load more list items:
                loadElements(currentPage);
            }
        }
    }
    

    dove si currentPagetrova la pagina dell'origine dati da aggiungere all'elenco e thresholdil numero di elementi dell'elenco (conteggiati alla fine) che, se visibili, dovrebbero attivare il processo di caricamento. Se si imposta thresholda 0, per esempio, l'utente deve scorrere fino alla fine della lista, al fine di caricare più elementi.

  3. (facoltativo) Come puoi vedere, il "controllo di caricamento più" viene chiamato solo quando l'utente smette di scorrere. Per migliorare l'usabilità, è possibile gonfiare e aggiungere un indicatore di caricamento alla fine dell'elenco tramite listView.addFooterView(yourFooterView). Un esempio per tale vista a piè di pagina:

    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/footer_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" >
    
        <ProgressBar
            android:id="@+id/progressBar1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/progressBar1"
            android:padding="5dp"
            android:text="@string/loading_text" />
    
    </RelativeLayout>
    
  4. (facoltativo) Infine, rimuovi quell'indicatore di caricamento chiamando listView.removeFooterView(yourFooterView)se non ci sono più elementi o pagine.


Ho provato questo, ma non funziona per ListFragment. Come può essere utilizzato in ListFragment.
Zeeshan,

Bene, lo uso anche con ListFragmentsenza problemi - basta seguire ciascuno dei passaggi sopra e andrà tutto bene;) O potresti fornire messaggi di errore?
saschoar,

Che cosa si potrebbe avere perso è spiegato in questa risposta SO: stackoverflow.com/a/6357957/1478093 -getListView().setOnScrollListener(this)
TBieniek

7
nota: l'aggiunta e la rimozione di piè di pagina su listview è problematica (il piè di pagina non viene visualizzato se setAdapter viene chiamato prima di aggiungere piè di pagina), ho finito per modificare la visibilità della vista di piè di pagina direttamente senza rimuoverla.
dvd,

Cosa deve essere aggiunto in loadElements (currentPage) ??? Voglio dire, chiamo il mio AsyncTask o un thread?
android_developer

20

È possibile rilevare la fine dell'elenco con l'aiuto di onScrollListener , il codice di lavoro è presentato di seguito:

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (view.getAdapter() != null && ((firstVisibleItem + visibleItemCount) >= totalItemCount) && totalItemCount != mPrevTotalItemCount) {
        Log.v(TAG, "onListEnd, extending list");
        mPrevTotalItemCount = totalItemCount;
        mAdapter.addMoreData();
    }
}

Un altro modo per farlo (all'interno dell'adattatore) è il seguente:

    public View getView(int pos, View v, ViewGroup p) {
            if(pos==getCount()-1){
                addMoreData(); //should be asynctask or thread
            }
            return view;
    }

Tieni presente che questo metodo verrà chiamato molte volte, quindi devi aggiungere un'altra condizione per bloccare più chiamate addMoreData().

Quando aggiungi tutti gli elementi all'elenco, chiama notifyDataSetChanged () all'interno dell'adattatore per aggiornare la vista (dovrebbe essere eseguito sul thread dell'interfaccia utente - runOnUiThread )


7
È una cattiva pratica fare altre cose oltre a restituire una vista getView. Ad esempio, non sei garantito che posvenga visualizzato dall'utente. Potrebbe essere semplicemente la misurazione di ListView.
Thomas Ahle,

Voglio caricare più articoli dopo che sono stati fatti scorrere 10 articoli. ma noto che il caricamento inizia prima che il decimo elemento sia visibile significa sul nono elemento. Quindi, come fermarlo?
Atul Bhardwaj,

4

A Ognyan Bankov GitHubho trovato una soluzione semplice e funzionante!

Si avvale di Volley HTTP libraryciò che rende la rete per le app Android più semplice e, soprattutto, più veloce. Volley è disponibile tramite il repository AOSP aperto.

Il codice fornito dimostra:

  1. ListView popolato da richieste impaginate HTTP.
  2. Utilizzo di NetworkImageView.
  3. Impaginazione ListView "senza fine" con read-ahead.

Per consistenza futura ho modificato il repository di Bankov .


2

Ecco una soluzione che semplifica anche la visualizzazione di una vista di caricamento alla fine di ListView durante il caricamento.

Puoi vedere le lezioni qui:

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - Helper per rendere possibile l'uso di funzioni senza estensione da SimpleListViewWithLoadingIndicator.

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - Listener che inizia a caricare i dati quando l'utente sta per raggiungere la parte inferiore di ListView.

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Puoi usare questa classe direttamente o estenderla.


1

Potrebbe essere un po 'tardi, ma la seguente soluzione è stata molto utile nel mio caso. In un certo senso, tutto ciò che devi fare è aggiungere a ListView a Footere crearlo addOnLayoutChangeListener.

http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)

Per esempio:

ListView listView1 = (ListView) v.findViewById(R.id.dialogsList); // Your listView
View loadMoreView = getActivity().getLayoutInflater().inflate(R.layout.list_load_more, null); // Getting your layout of FooterView, which will always be at the bottom of your listview. E.g. you may place on it the ProgressBar or leave it empty-layout.
listView1.addFooterView(loadMoreView); // Adding your View to your listview 

...

loadMoreView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
         Log.d("Hey!", "Your list has reached bottom");
    }
});

Questo evento si attiva una volta quando un piè di pagina diventa visibile e funziona come un incantesimo.


0

La chiave di questo problema è rilevare l'evento load-more, avviare una richiesta asincrona per i dati e quindi aggiornare l'elenco. Inoltre è necessario un adattatore con indicatore di caricamento e altri decoratori. In effetti, il problema è molto complicato in alcuni casi angolari. Solo OnScrollListenerun'implementazione non è sufficiente, perché a volte gli elementi non riempiono lo schermo.

Ho scritto un pacchetto personale che supporta un elenco infinito RecyclerViewe fornisce anche un'implementazione del caricatore asincrono AutoPagerFragmentche rende molto facile ottenere dati da una fonte multi-pagina. Può caricare qualsiasi pagina in un RecyclerViewevento personalizzato, non solo la pagina successiva.

Ecco l'indirizzo: https://github.com/SphiaTower/AutoPagerRecyclerManager


Il collegamento è interrotto.
DSlomer64,


0

Ho lavorato in un'altra soluzione molto simile a quella, ma, sto usando un footerViewper dare la possibilità all'utente di scaricare più elementi facendo clic su footerView, sto usando un "menu" che viene mostrato sopra ListViewe nella parte inferiore della vista genitore, questo "menu" nasconde la parte inferiore del ListView, quindi, quando il listViewmenu a scorrimento scorre scompare e quando lo stato di scorrimento è inattivo, il menu appare di nuovo, ma quando l'utente scorre fino alla fine del listView, "Chiedo" di sapere se footerViewviene visualizzato in quel caso, il menu non viene visualizzato e l'utente può vedere il footerViewcaricamento di più contenuti. Ecco il codice:

Saluti.

listView.setOnScrollListener(new OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        if(scrollState == SCROLL_STATE_IDLE) {
            if(footerView.isShown()) {
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            } else {
                bottomView.setVisibility(LinearLayout.VISIBLE);
            } else {
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {

    }
});

0

So che è una vecchia domanda e il mondo Android è passato principalmente a RecyclerViews, ma per chiunque sia interessato, potresti trovare questa libreria molto interessante.

Utilizza BaseAdapter utilizzato con ListView per rilevare quando l'elenco è stato fatto scorrere fino all'ultimo elemento o quando è stato fatto scorrere lontano dall'ultimo elemento.

Viene fornito con un progetto di esempio (appena 100 righe di codice attività) che può essere utilizzato per capire rapidamente come funziona.

Semplice utilizzo:

class Boy{

private String name;
private double height;
private int age;
//Other code

}

Un adattatore per contenere oggetti Boy sarebbe simile a:


public class BoysAdapter extends EndlessAdapter<Boy>{




        ViewHolder holder = null;


        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent
                    .getContext());

            holder = new ViewHolder();

            convertView = inflater.inflate(
                    R.layout.list_cell, parent, false);


            holder.nameView = convertView.findViewById(R.id.cell);

            // minimize the default image.
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Boy boy = getItem(position);

        try {
            holder.nameView.setText(boy.getName());

            ///Other data rendering codes.

        } catch (Exception e) {
            e.printStackTrace();
        }

        return super.getView(position,convertView,parent);

}

Notare come il metodo getView di BoysAdapter restituisce una chiamata al getViewmetodo della superclasse EndlessAdapter . Questo è essenziale al 100%.

Ora per creare l'adattatore, eseguire:

   adapter = new ModelAdapter() {
            @Override
            public void onScrollToBottom(int bottomIndex, boolean moreItemsCouldBeAvailable) {

                if (moreItemsCouldBeAvailable) { 
                    makeYourServerCallForMoreItems();
                } else {
                    if (loadMore.getVisibility() != View.VISIBLE) {
                        loadMore.setVisibility(View.VISIBLE);
                    }
                }
            }

            @Override
            public void onScrollAwayFromBottom(int currentIndex) { 
                loadMore.setVisibility(View.GONE);
            }

            @Override
            public void onFinishedLoading(boolean moreItemsReceived) { 
                if (!moreItemsReceived) {
                    loadMore.setVisibility(View.VISIBLE);
                }
            }
        };

L' loadMoreelemento è un pulsante o un altro elemento dell'interfaccia utente su cui è possibile fare clic per recuperare più dati dall'URL. Se posizionato come descritto nel codice, l'adattatore sa esattamente quando mostrare quel pulsante e quando disabilitarlo. Basta creare il pulsante nel tuo XML e posizionarlo come mostrato nel codice dell'adattatore sopra.

Godere.

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.