Wie kann ich eine Liste erstellen, in der ich benachrichtigt werde, wenn Sie das Ende der Liste erreichen, damit ich weitere Elemente laden kann?
Wie kann ich eine Liste erstellen, in der ich benachrichtigt werde, wenn Sie das Ende der Liste erreichen, damit ich weitere Elemente laden kann?
Antworten:
Eine Lösung besteht darin, ein zu implementieren OnScrollListener
und Änderungen (wie das Hinzufügen von Elementen usw.) an dem ListAdapter
in einem geeigneten Zustand in seiner onScroll
Methode vorzunehmen .
Das Folgende ListActivity
zeigt 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).
firstVisible + visibleCount >= totalCount
bei 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.
Ich wollte nur eine Lösung beitragen, die ich für meine App verwendet habe.
Es basiert auch auf der OnScrollListener
Benutzeroberflä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.
ListFragment
oder ListActivity
implementierenOnScrollListener
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 currentPage
befindet sich die Seite Ihrer Datenquelle, die Ihrer Liste hinzugefügt werden soll, und threshold
die Anzahl der Listenelemente (vom Ende an gezählt), die, falls sichtbar, den Ladevorgang auslösen sollen. Wenn Sie setzen threshold
auf 0
, zum Beispiel, hat der Benutzer bis zum Ende der Liste , um scrollen , um weitere Elemente zu laden.
(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>
(optional) Entfernen Sie abschließend diese Ladeanzeige, indem Sie aufrufen, listView.removeFooterView(yourFooterView)
wenn keine Elemente oder Seiten mehr vorhanden sind.
ListFragment
auch ohne Probleme - folge einfach jedem der obigen Schritte und es wird dir gut gehen;) Oder könntest du irgendwelche Fehlermeldungen geben?
getListView().setOnScrollListener(this)
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 ).
getView
. Zum Beispiel können Sie nicht garantieren, dass dies pos
vom Benutzer angezeigt wird. Es könnte einfach ListView-Messmaterial sein.
Bei Ognyan Bankov GitHub
fand 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:
Für die zukünftige Konsistenz gabelte ich Bankovs Repo .
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.
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 Footer
und 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.
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 OnScrollListener
Implementierung reicht nicht aus, da die Elemente manchmal den Bildschirm nicht ausfüllen.
Ich habe ein persönliches Paket geschrieben, das endlose Listen RecyclerView
unterstützt und außerdem eine asynchrone Loader-Implementierung bereitstellt, mit AutoPagerFragment
der es sehr einfach ist, Daten aus einer mehrseitigen Quelle abzurufen. Es kann jede gewünschte Seite in ein RecyclerView
benutzerdefiniertes Ereignis laden , nicht nur die nächste Seite.
Hier ist die Adresse: https://github.com/SphiaTower/AutoPagerRecyclerManager
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.
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 footerView
ich auf klicke . Ich verwende ein "Menü", das über ListView
und unter dem angezeigt wird In der übergeordneten Ansicht verbirgt dieses "Menü" den unteren Rand des Menüs. ListView
Wenn listView
also 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 footerView
in 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) {
}
});
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 loadMore
Element 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.