Android Endless List

232

Wie kann ich eine Liste erstellen, in der ich benachrichtigt werde, wenn Sie das Ende der Liste erreichen, damit ich weitere Elemente laden kann?

Isaac Waller
quelle
9
Ich empfehle den EndlessAdapter von CommonWare: github.com/commonsguy/cwac-endless . Ich fand es aus dieser Antwort auf eine andere Frage: stackoverflow.com/questions/4667064/… .
Tyler Collier
Ich brauche das gleiche ... kann jemand helfen? .. stackoverflow.com/questions/28122304/…

Antworten:

269

Eine Lösung besteht darin, ein zu implementieren OnScrollListenerund Änderungen (wie das Hinzufügen von Elementen usw.) an dem ListAdapterin einem geeigneten Zustand in seiner onScrollMethode vorzunehmen .

Das Folgende ListActivityzeigt eine Liste von Ganzzahlen, beginnend mit 40, und fügt Elemente hinzu, wenn der Benutzer zum Ende der Liste blättert.

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

Sie sollten natürlich separate Threads für Aktionen mit langer Laufzeit verwenden (z. B. das Laden von Webdaten) und möglicherweise den Fortschritt des letzten Listenelements anzeigen (wie dies bei Markt- oder Google Mail-Apps der Fall ist).

Josef Pfleger
quelle
3
Huh, würde dies nicht die Vergrößerung des Adapters auslösen, wenn Sie aufhören würden, in der Mitte des Bildschirms zu scrollen? Es sollte nur die nächsten 10 laden, wenn es tatsächlich die Liste unten erreicht.
Matthias
9
Ich bemerke viele Beispiele wie dieses mit BaseAdapter. Wenn Sie eine Datenbank verwenden, möchten Sie einen SimpleCursorAdapter verwenden. Wenn Sie der Liste hinzufügen müssen, aktualisieren Sie Ihre Datenbank, rufen Sie einen neuen Cursor ab und verwenden Sie SimpleCursorAdapter # changeCursor zusammen mit notifyDataSetChanged. Es ist keine Unterklasse erforderlich und die Leistung ist besser als bei einem BaseAdapter (wiederum nur, wenn Sie eine Datenbank verwenden).
Brack
1
Nach dem Aufruf von notifyDataSetChanged () steht es ganz oben auf der Liste. Wie kann ich dies verhindern?
Unentschieden
2
Ein Hinweis - Ich habe diesen Ansatz verwendet, musste jedoch eine Überprüfung hinzufügen, ob sichtbarer Wert 0 ist. Für jede Liste erhalte ich einen Rückruf an onScroll, wobei firstVisible, sichtbarer Betrag und totalCount alle 0 sind, was technisch die Bedingung erfüllt, dies jedoch nicht ist wenn ich mehr laden will.
Eric Mill
5
Ein weiteres Problem mit diesem Code besteht darin, dass die Bedingung firstVisible + visibleCount >= totalCountbei mehreren Aufrufen des Listeners mehrmals erfüllt wird. Wenn es sich bei der Funktion "Mehr laden" um eine Webanforderung handelt (höchstwahrscheinlich), fügen Sie eine weitere Überprüfung hinzu, ob eine Anforderung ausgeführt wird oder nicht. Überprüfen Sie andererseits, ob die Gesamtanzahl nicht der vorherigen Gesamtanzahl entspricht, da nicht mehrere Anforderungen für dieselbe Anzahl von Elementen in der Liste erforderlich sind.
Subin Sebastian
94

Ich wollte nur eine Lösung beitragen, die ich für meine App verwendet habe.

Es basiert auch auf der OnScrollListenerBenutzeroberfläche, aber ich habe festgestellt, dass es auf Low-End-Geräten eine viel bessere Bildlaufleistung aufweist, da während der Bildlaufvorgänge keine der Berechnungen für die sichtbare Anzahl / Gesamtanzahl durchgeführt werden.

  1. Lassen Sie Ihre ListFragmentoder ListActivityimplementierenOnScrollListener
  2. Fügen Sie dieser Klasse die folgenden Methoden hinzu:

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

    Wo currentPagebefindet sich die Seite Ihrer Datenquelle, die Ihrer Liste hinzugefügt werden soll, und thresholddie Anzahl der Listenelemente (vom Ende an gezählt), die, falls sichtbar, den Ladevorgang auslösen sollen. Wenn Sie setzen thresholdauf 0, zum Beispiel, hat der Benutzer bis zum Ende der Liste , um scrollen , um weitere Elemente zu laden.

  3. (optional) Wie Sie sehen, wird die "Mehrlastprüfung" nur aufgerufen, wenn der Benutzer den Bildlauf beendet. Um die Benutzerfreundlichkeit zu verbessern, können Sie über aufblasen und am Ende der Liste einen Ladeindikator hinzufügen listView.addFooterView(yourFooterView). Ein Beispiel für eine solche Fußzeilenansicht:

    <?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. (optional) Entfernen Sie abschließend diese Ladeanzeige, indem Sie aufrufen, listView.removeFooterView(yourFooterView)wenn keine Elemente oder Seiten mehr vorhanden sind.

Saschoar
quelle
Ich habe es versucht, aber es funktioniert nicht für ListFragment. Wie kann es in ListFragment verwendet werden?
Zeeshan
Nun, ich benutze es ListFragmentauch ohne Probleme - folge einfach jedem der obigen Schritte und es wird dir gut gehen;) Oder könntest du irgendwelche Fehlermeldungen geben?
Saschoar
Was Sie möglicherweise verpasst haben, wird in dieser SO-Antwort erklärt: stackoverflow.com/a/6357957/1478093 -getListView().setOnScrollListener(this)
TBieniek
7
Hinweis: Das Hinzufügen und Entfernen der Fußzeile in der Listenansicht ist problematisch (die Fußzeile wird nicht angezeigt, wenn setAdapter vor dem Hinzufügen der Fußzeile aufgerufen wird). Am Ende habe ich die Sichtbarkeit der Fußzeilenansicht direkt geändert, ohne sie zu entfernen.
DVD
Was muss in den loadElements (currentPage) hinzugefügt werden ??? Ich meine, rufe ich meine AsyncTask oder einen Thread auf?
android_developer
20

Sie können das Ende der Liste mithilfe von onScrollListener erkennen. Der Arbeitscode wird unten dargestellt:

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

Eine andere Möglichkeit, dies zu tun (innerhalb des Adapters), ist wie folgt:

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

Beachten Sie, dass diese Methode häufig aufgerufen wird. Sie müssen daher eine weitere Bedingung hinzufügen, um mehrere Aufrufe von zu blockieren addMoreData().

Wenn Sie alle Elemente zur Liste hinzufügen, rufen Sie notifyDataSetChanged () in Ihrem Adapter auf, um die Ansicht zu aktualisieren (sie sollte auf dem UI-Thread ausgeführt werden - runOnUiThread ).

Dariusz Bacinski
quelle
7
Es ist eine schlechte Praxis, andere Dinge zu tun, als eine Ansicht zurückzugeben getView. Zum Beispiel können Sie nicht garantieren, dass dies posvom Benutzer angezeigt wird. Es könnte einfach ListView-Messmaterial sein.
Thomas Ahle
Ich möchte mehr Elemente laden, nachdem 10 Elemente gescrollt wurden. Ich stelle jedoch fest, dass das Laden beginnt, bevor der 10. Artikel sichtbar ist. Dies bedeutet, dass der 9. Artikel angezeigt wird. Wie kann man dies stoppen?
Atul Bhardwaj
4

Bei Ognyan Bankov GitHubfand ich eine einfache und funktionierende Lösung!

Es nutzt das Volley HTTP library, was das Networking für Android-Apps einfacher und vor allem schneller macht. Volley ist über das offene AOSP-Repository verfügbar.

Der angegebene Code zeigt:

  1. ListView, die mit HTTP-paginierten Anforderungen gefüllt ist.
  2. Verwendung von NetworkImageView.
  3. "Endlose" ListView-Paginierung mit Vorauslesen.

Für die zukünftige Konsistenz gabelte ich Bankovs Repo .

oikonomopo
quelle
2

Hier ist eine Lösung, die es auch einfach macht, eine Ladeansicht am Ende der ListView anzuzeigen, während sie geladen wird.

Sie können die Klassen hier sehen:

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - Hilfsprogramm zur Verwendung des Funktionen ohne Erweiterung von SimpleListViewWithLoadingIndicator.

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - Listener, der Daten lädt, wenn der Benutzer ist im Begriff, den unteren Rand der ListView zu erreichen.

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Sie können diese Klasse direkt verwenden oder erweitern.

Fernando Camargo
quelle
1

Es mag etwas spät sein, aber die folgende Lösung ist in meinem Fall sehr nützlich. In gewisser Weise müssen Sie nur Ihre ListView a hinzufügen Footerund dafür erstellen addOnLayoutChangeListener.

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

Beispielsweise:

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

Dieses Ereignis wird einmal ausgelöst, wenn eine Fußzeile sichtbar wird und wie ein Zauber wirkt.

tehcpu
quelle
0

Der Schlüssel zu diesem Problem besteht darin, das Ereignis "Mehr laden" zu erkennen, eine asynchrone Anforderung für Daten zu starten und dann die Liste zu aktualisieren. Außerdem wird ein Adapter mit Ladeanzeige und anderen Dekorateuren benötigt. Tatsächlich ist das Problem in einigen Eckfällen sehr kompliziert. Nur eine OnScrollListenerImplementierung reicht nicht aus, da die Elemente manchmal den Bildschirm nicht ausfüllen.

Ich habe ein persönliches Paket geschrieben, das endlose Listen RecyclerViewunterstützt und außerdem eine asynchrone Loader-Implementierung bereitstellt, mit AutoPagerFragmentder es sehr einfach ist, Daten aus einer mehrseitigen Quelle abzurufen. Es kann jede gewünschte Seite in ein RecyclerViewbenutzerdefiniertes Ereignis laden , nicht nur die nächste Seite.

Hier ist die Adresse: https://github.com/SphiaTower/AutoPagerRecyclerManager

ProtossShuttle
quelle
Die Verbindung ist unterbrochen.
DSlomer64
0

Die beste Lösung, die ich bisher gesehen habe, ist die FastAdapter- Bibliothek für Recycler-Ansichten. Es hat eine EndlessRecyclerOnScrollListener.

Hier ist ein Beispiel für die Verwendung: EndlessScrollListActivity

Als ich es für endlose Bildlauflisten verwendet habe, habe ich festgestellt, dass das Setup sehr robust ist. Ich würde es auf jeden Fall empfehlen.

Shubham Chaudhary
quelle
0

Ich habe in einer anderen Lösung gearbeitet, die dieser sehr ähnlich ist, aber ich verwende a footerView, um dem Benutzer die Möglichkeit zu geben, mehr Elemente herunterzuladen, indem footerViewich auf klicke . Ich verwende ein "Menü", das über ListViewund unter dem angezeigt wird In der übergeordneten Ansicht verbirgt dieses "Menü" den unteren Rand des Menüs. ListViewWenn listViewalso das Menü gescrollt wird und der Bildlaufstatus inaktiv ist, wird das Menü erneut angezeigt. Wenn der Benutzer jedoch zum Ende des Menüs blättert listView, "frage" ich nach Wenn footerViewin diesem Fall das angezeigt wird, wird das Menü nicht angezeigt und der Benutzer kann sehen footerView, dass weitere Inhalte geladen werden. Hier der Code:

Grüße.

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

    }
});
pabloverd
quelle
0

Ich weiß, es ist eine alte Frage und die Android-Welt ist größtenteils zu RecyclerViews übergegangen, aber für alle Interessierten mag diese Bibliothek sehr interessant sein.

Es verwendet den BaseAdapter, der mit der ListView verwendet wird, um zu erkennen, wann die Liste zum letzten Element gescrollt wurde oder wann vom letzten Element weg gescrollt wird.

Es wird mit einem Beispielprojekt (kaum 100 Zeilen Aktivitätscode) geliefert, mit dem Sie schnell verstehen können, wie es funktioniert.

Einfache Verwendung:

class Boy{

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

}

Ein Adapter zum Halten von Boy-Objekten würde folgendermaßen aussehen:


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

}

Beachten Sie, wie die getView-Methode des BoysAdapter einen Aufruf der Methode der Superklasse EndlessAdapter zurückgibt getView. Dies ist zu 100% wichtig.

Um den Adapter zu erstellen, gehen Sie wie folgt vor:

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

Das loadMoreElement ist eine Schaltfläche oder ein anderes UI-Element, auf das geklickt werden kann, um weitere Daten aus der URL abzurufen. Wenn der Adapter wie im Code beschrieben platziert wird, weiß er genau, wann diese Schaltfläche angezeigt und wann sie deaktiviert werden muss. Erstellen Sie einfach die Schaltfläche in Ihrer XML-Datei und platzieren Sie sie wie im obigen Adaptercode gezeigt.

Genießen.

Gbenroscience
quelle