¿Cómo puedo crear una lista donde cuando llegue al final de la lista se me notifique para poder cargar más elementos?
¿Cómo puedo crear una lista donde cuando llegue al final de la lista se me notifique para poder cargar más elementos?
Respuestas:
Una solución es implementar OnScrollListener
y realizar cambios (como agregar elementos, etc.) al ListAdapter
estado conveniente en su onScroll
método.
A continuación se ListActivity
muestra una lista de enteros, comenzando con 40, agregando elementos cuando el usuario se desplaza hasta el final de la lista.
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;
}
}
}
Obviamente, debe usar hilos separados para acciones de larga duración (como cargar datos web) y puede que desee indicar el progreso en el último elemento de la lista (como lo hacen las aplicaciones de mercado o gmail).
firstVisible + visibleCount >= totalCount
se cumple varias veces con múltiples llamadas al oyente. Si la función de cargar más es una solicitud web, (lo más probable es que lo sea), agregue otra verificación para ver si una solicitud continúa o no. Por otro lado, verifique si totalCount no es igual al recuento total anterior, porque no necesitamos múltiples solicitudes para el mismo número de elementos en la lista.
Solo quería aportar una solución que utilicé para mi aplicación.
También se basa en la OnScrollListener
interfaz, pero descubrí que tiene un rendimiento de desplazamiento mucho mejor en dispositivos de gama baja, ya que ninguno de los cálculos de conteo visible / total se realiza durante las operaciones de desplazamiento.
ListFragment
o ListActivity
implementeOnScrollListener
Agregue los siguientes métodos a esa clase:
@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);
}
}
}
dónde currentPage
está la página de su fuente de datos que debe agregarse a su lista, y threshold
es el número de elementos de la lista (contados desde el final) que deberían, si están visibles, desencadenar el proceso de carga. Si se establece threshold
a 0
, por ejemplo, el usuario tiene que desplazarse hasta el final de la lista con el fin de cargar más elementos.
(opcional) Como puede ver, la "comprobación de carga más" solo se llama cuando el usuario deja de desplazarse. Para mejorar la usabilidad, puede inflar y agregar un indicador de carga al final de la lista a través de listView.addFooterView(yourFooterView)
. Un ejemplo para tal vista de pie de página:
<?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>
(opcional) Finalmente, elimine ese indicador de carga llamando listView.removeFooterView(yourFooterView)
si no hay más elementos o páginas.
ListFragment
sin ningún problema, solo siga cada uno de los pasos anteriores y estará bien;) ¿O podría proporcionar algún mensaje de error?
getListView().setOnScrollListener(this)
Puede detectar el final de la lista con la ayuda de onScrollListener , el código de trabajo se presenta a continuación:
@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();
}
}
Otra forma de hacerlo (adaptador interno) es la siguiente:
public View getView(int pos, View v, ViewGroup p) {
if(pos==getCount()-1){
addMoreData(); //should be asynctask or thread
}
return view;
}
Tenga en cuenta que este método se llamará muchas veces, por lo que debe agregar otra condición para bloquear varias llamadas addMoreData()
.
Cuando agrega todos los elementos a la lista, llame a notifyDataSetChanged () dentro de su adaptador para actualizar la Vista (debe ejecutarse en el hilo de la interfaz de usuario - runOnUiThread )
getView
. Por ejemplo, no está garantizado que pos
sea visto por el usuario. Podría ser simplemente cosas de medición ListView.
¡ Ognyan Bankov GitHub
Encontré una solución simple y funcional!
Utiliza el Volley HTTP library
que hace que la creación de redes para aplicaciones de Android sea más fácil y, lo más importante, más rápida. Volley está disponible a través del repositorio abierto de AOSP.
El código dado demuestra:
Para una consistencia futura, bifurqué el repositorio de Bankov .
Aquí hay una solución que también facilita mostrar una vista de carga al final de ListView mientras se carga.
Puedes ver las clases aquí:
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - Ayudante para que sea posible utilizar el características sin extender desde SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - Oyente que comienza a cargar datos cuando el usuario está a punto de llegar al final de ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Puede usar esta clase directamente o ampliarla.
Puede llegar un poco tarde, pero la siguiente solución fue muy útil en mi caso. En cierto modo, todo lo que necesita hacer es agregar a su ListView a Footer
y crear para él addOnLayoutChangeListener
.
http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)
Por ejemplo:
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");
}
});
Este evento se dispara una vez cuando un pie de página se hace visible y funciona de maravilla.
La clave de este problema es detectar el evento de cargar más, iniciar una solicitud asíncrona de datos y luego actualizar la lista. También se necesita un adaptador con indicador de carga y otros decoradores. De hecho, el problema es muy complicado en algunos casos de esquina. Solo una OnScrollListener
implementación no es suficiente, porque a veces los elementos no llenan la pantalla.
He escrito un paquete personal que admite una lista interminable RecyclerView
y también proporciona una implementación de cargador asíncrono AutoPagerFragment
que hace que sea muy fácil obtener datos de una fuente de varias páginas. Puede cargar cualquier página que desee en un RecyclerView
evento personalizado, no solo la página siguiente.
Aquí está la dirección: https://github.com/SphiaTower/AutoPagerRecyclerManager
La mejor solución hasta ahora que he visto es en la biblioteca FastAdapter para vistas de reciclador. Tiene un EndlessRecyclerOnScrollListener
.
Aquí hay un ejemplo de uso: EndlessScrollListActivity
Una vez que lo usé para una lista de desplazamiento sin fin, me di cuenta de que la configuración es muy robusta. Definitivamente lo recomendaría.
He estado trabajando en otra solución muy similar a esa, pero estoy usando a footerView
para dar la posibilidad al usuario de descargar más elementos haciendo clic en footerView
, estoy usando un "menú" que se muestra arriba ListView
y en la parte inferior de vista principal, este "menú" oculta la parte inferior del ListView
, por lo tanto, cuando listView
se desplaza el menú desaparece y cuando el estado de desplazamiento está inactivo, el menú aparece de nuevo, pero cuando el usuario se desplaza hasta el final del listView
, "pregunto" saber si footerView
se muestra en ese caso, el menú no aparece y el usuario puede ver footerView
para cargar más contenido. Aquí el código:
Saludos.
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) {
}
});
Sé que es una pregunta antigua y el mundo de Android se ha movido principalmente a RecyclerViews, pero para cualquier persona interesada, puede encontrar esta biblioteca muy interesante.
Utiliza el Adaptador base utilizado con ListView para detectar cuándo la lista se ha desplazado al último elemento o cuándo se está desplazando fuera del último elemento.
Viene con un proyecto de ejemplo (apenas 100 líneas de código de actividad) que se puede utilizar para comprender rápidamente cómo funciona.
Uso simple:
class Boy{
private String name;
private double height;
private int age;
//Other code
}
Un adaptador para sostener objetos Boy se vería así:
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);
}
Observe cómo el método getView de BoysAdapter devuelve una llamada al getView
método de la superclase EndlessAdapter . Esto es 100% esencial.
Ahora para crear el adaptador, haga:
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);
}
}
};
El loadMore
elemento es un botón u otro elemento ui en el que se puede hacer clic para obtener más datos de la URL. Cuando se coloca como se describe en el código, el adaptador sabe exactamente cuándo mostrar ese botón y cuándo deshabilitarlo. Simplemente cree el botón en su xml y colóquelo como se muestra en el código del adaptador anterior.
Disfrutar.