Jak mogę utworzyć listę, w której po osiągnięciu końca listy otrzymam powiadomienie, żebym mógł załadować więcej elementów?
Jak mogę utworzyć listę, w której po osiągnięciu końca listy otrzymam powiadomienie, żebym mógł załadować więcej elementów?
Odpowiedzi:
Jednym z rozwiązań jest wdrożenie OnScrollListener
i wprowadzenie zmian (takich jak dodawanie elementów itp.) ListAdapter
W dogodnym stanie w tej onScroll
metodzie.
Poniżej ListActivity
przedstawiono listę liczb całkowitych, zaczynając od 40, dodając elementy, gdy użytkownik przewija do końca listy.
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;
}
}
}
Oczywiście powinieneś używać osobnych wątków do długotrwałych działań (takich jak ładowanie danych internetowych) i możesz chcieć wskazać postęp w ostatnim elemencie listy (tak jak robią to aplikacje market lub Gmail).
firstVisible + visibleCount >= totalCount
jest spełniony wiele razy przy wielu wywołaniach do nasłuchiwania. Jeśli funkcja ładowania więcej jest żądaniem internetowym (najprawdopodobniej będzie), dodaj kolejne sprawdzenie, czy żądanie trwa, czy nie. Z drugiej strony sprawdź, czy totalCount nie jest równy Poprzedniej sumie, ponieważ nie potrzebujemy wielu żądań dla tej samej liczby pozycji na liście.
Chciałem tylko udostępnić rozwiązanie, którego użyłem w mojej aplikacji.
Opiera się również na OnScrollListener
interfejsie, ale zauważyłem, że ma on znacznie lepszą wydajność przewijania na niższych urządzeniach, ponieważ podczas operacji przewijania nie są wykonywane żadne obliczenia widoczne / całkowite zliczanie.
ListFragment
lub ListActivity
wdrożyćOnScrollListener
Dodaj następujące metody do tej klasy:
@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);
}
}
}
gdzie currentPage
znajduje się strona źródła danych, które należy dodać do listy, i threshold
liczba elementów listy (liczona od końca), które powinny, jeśli są widoczne, uruchomić proces ładowania. Jeśli ustawisz threshold
się 0
, na przykład, użytkownik musi przejść na samym końcu listy, aby załadować więcej rzeczy.
(opcjonalnie) Jak widać, „ładowanie więcej kontroli” jest wywoływane tylko wtedy, gdy użytkownik przestanie przewijać. Aby poprawić użyteczność, możesz napompować i dodać wskaźnik ładowania na końcu listy za pośrednictwem listView.addFooterView(yourFooterView)
. Jeden przykład takiego widoku stopki:
<?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>
(opcjonalnie) Na koniec usuń ten wskaźnik ładowania, dzwoniąc, listView.removeFooterView(yourFooterView)
jeśli nie ma już więcej elementów lub stron.
ListFragment
również bez żadnych problemów - wystarczy wykonać każdy z powyższych kroków, a wszystko będzie dobrze;) Czy możesz podać jakieś komunikaty o błędach?
getListView().setOnScrollListener(this)
Koniec listy można wykryć przy pomocy onScrollListener , działający kod przedstawiono poniżej:
@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();
}
}
Innym sposobem na to (wewnętrzny adapter) jest:
public View getView(int pos, View v, ViewGroup p) {
if(pos==getCount()-1){
addMoreData(); //should be asynctask or thread
}
return view;
}
Pamiętaj, że ta metoda będzie wywoływana wiele razy, więc musisz dodać kolejny warunek, aby zablokować wiele wywołań addMoreData()
.
Po dodaniu wszystkich elementów do listy należy wywołać powiadomienie DataSetChanged () wewnątrz adaptera, aby zaktualizować widok (powinien być uruchamiany w wątku interfejsu użytkownika - runOnUiThread )
getView
. Na przykład nie masz gwarancji, że pos
użytkownik wyświetli. Może to być po prostu pomiar ListView.
W Ognyan Bankov GitHub
znalazłem proste i działające rozwiązanie!
Wykorzystuje to, Volley HTTP library
co sprawia, że tworzenie sieci dla aplikacji na Androida jest łatwiejsze i, co najważniejsze, szybsze. Siatkówka jest dostępna za pośrednictwem otwartego repozytorium AOSP.
Podany kod pokazuje:
Aby zachować spójność w przyszłości, rozwidliłem repozytorium Bankowa .
Oto rozwiązanie, które ułatwia wyświetlanie widoku ładowania na końcu ListView podczas ładowania.
Możesz zobaczyć zajęcia tutaj:
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - Pomocnik umożliwiający korzystanie z funkcje bez rozszerzania z SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - Słuchacz, który rozpoczyna ładowanie danych, gdy użytkownik zbliża się do dolnej części ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Możesz użyć tej klasy bezpośrednio lub ją rozszerzyć.
Może trochę się spóźnić, ale w moim przypadku bardzo przydatne okazało się następujące rozwiązanie. W ten sposób wszystko, co musisz zrobić, to dodać do ListView a Footer
i stworzyć dla niego addOnLayoutChangeListener
.
http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)
Na przykład:
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");
}
});
To wydarzenie jest uruchamiane raz, gdy stopka staje się widoczna i działa jak urok.
Kluczem do tego problemu jest wykrycie zdarzenia ładowania obciążenia, uruchomienie żądania asynchronicznego danych, a następnie zaktualizowanie listy. Potrzebny jest również adapter ze wskaźnikiem obciążenia i innymi dekoratorami. W rzeczywistości problem jest bardzo skomplikowany w niektórych przypadkach narożnych. Tylko OnScrollListener
realizacja nie wystarczy, bo czasami elementy nie wypełnia ekranu.
Napisałem osobisty pakiet, który obsługuje nieskończoną listę RecyclerView
, a także zapewnia implementację modułu ładującego asynchron, AutoPagerFragment
który bardzo ułatwia pobieranie danych ze źródła wielostronicowego. Może załadować dowolną stronę RecyclerView
na niestandardowe zdarzenie, nie tylko następną stronę.
Oto adres: https://github.com/SphiaTower/AutoPagerRecyclerManager
Najlepsze rozwiązanie, jakie do tej pory widziałem, znajduje się w bibliotece FastAdapter dla widoków recyklerów. Ma EndlessRecyclerOnScrollListener
.
Oto przykładowe użycie: EndlessScrollListActivity
Kiedyś użyłem go do nieskończonej przewijanej listy, zdałem sobie sprawę, że konfiguracja jest bardzo solidna. Zdecydowanie polecam.
Pracowałem w innym rozwiązaniu bardzo podobnym do tego, ale używam a, footerView
aby dać użytkownikowi możliwość pobrania większej liczby elementów, klikając przycisk footerView
, używam „menu” pokazanego powyżej ListView
i na dole widok rodzica, to „menu” ukrywa dolną część ListView
, więc gdy listView
przewijane menu zniknie, a gdy stan przewijania jest bezczynny, menu pojawi się ponownie, ale gdy użytkownik przewinie do końca listView
, „pytam”, aby wiedzieć, czy footerView
w takim przypadku jest pokazane, menu nie pojawia się, a użytkownik może zobaczyć, footerView
aby załadować więcej treści. Oto kod:
Pozdrowienia.
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) {
}
});
Wiem, że to stare pytanie, a świat Androida przeszedł w większości na RecyclerViews, ale dla wszystkich zainteresowanych ta biblioteka może być bardzo interesująca.
Wykorzystuje BaseAdapter używany z ListView do wykrywania, kiedy lista została przewinięta do ostatniego elementu lub kiedy jest przewijana od ostatniego elementu.
Zawiera przykładowy projekt (zaledwie 100 wierszy kodu działania), którego można użyć, aby szybko zrozumieć, jak to działa.
Proste użycie:
class Boy{
private String name;
private double height;
private int age;
//Other code
}
Adapter do przechowywania obiektów Boy wyglądałby następująco:
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);
}
Zwróć uwagę, w jaki sposób metoda getView BoysAdapter zwraca wywołanie do metody nadklasy EndlessAdapter getView
. Jest to w 100% niezbędne.
Teraz, aby utworzyć adapter, wykonaj:
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);
}
}
};
loadMore
Pozycja jest przycisk lub inny element interfejsu użytkownika, które można kliknąć, aby pobrać więcej danych z adresu URL. Po umieszczeniu zgodnie z opisem w kodzie adapter dokładnie wie, kiedy pokazać ten przycisk, a kiedy go wyłączyć. Po prostu utwórz przycisk w swoim pliku XML i umieść go tak, jak pokazano w powyższym kodzie adaptera.
Cieszyć się.