Lista sin fin de Android

Respuestas:

269

Una solución es implementar OnScrollListenery realizar cambios (como agregar elementos, etc.) al ListAdapterestado conveniente en su onScrollmétodo.

A continuación se ListActivitymuestra 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).

Josef Pfleger
fuente
3
¿No dispararía esto el aumento del tamaño del adaptador si dejaras de desplazarte en el medio de la pantalla? solo debe cargar los próximos 10 cuando realmente llegue al final de la lista.
Matthias
99
Noto muchos ejemplos como este usando BaseAdapter. Quería mencionar que si está utilizando una base de datos, intente usar un SimpleCursorAdapter. Cuando necesite agregar a la lista, actualice su base de datos, obtenga un nuevo cursor y use SimpleCursorAdapter # changeCursor junto con notifyDataSetChanged. No se requieren subclases, y funciona mejor que un Adaptador de Base (de nuevo, solo si está utilizando una base de datos).
Brack
1
Después de llamar a notifyDataSetChanged (), irá al principio de la lista, ¿cómo puedo evitarlo?
sorteo del
2
Una nota: utilicé este enfoque, pero necesitaba agregar una verificación para cuando visibleCount es 0. Para cada lista, recibo una devolución de llamada a onScroll donde firstVisible, visibleCount y totalCount son todos 0, que técnicamente cumple con el condicional, pero no es cuando quiero cargar más
Eric Mill
55
Otro problema con este código es que la condición firstVisible + visibleCount >= totalCountse 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.
Subin Sebastian
94

Solo quería aportar una solución que utilicé para mi aplicación.

También se basa en la OnScrollListenerinterfaz, 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.

  1. Deje que su ListFragmento ListActivityimplementeOnScrollListener
  2. 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 currentPageestá la página de su fuente de datos que debe agregarse a su lista, y thresholdes 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 thresholda 0, por ejemplo, el usuario tiene que desplazarse hasta el final de la lista con el fin de cargar más elementos.

  3. (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>
  4. (opcional) Finalmente, elimine ese indicador de carga llamando listView.removeFooterView(yourFooterView)si no hay más elementos o páginas.

Saschoar
fuente
Intenté esto pero no funciona para ListFragment. ¿Cómo se puede usar en ListFragment?
zeeshan
Bueno, también lo uso ListFragmentsin ningún problema, solo siga cada uno de los pasos anteriores y estará bien;) ¿O podría proporcionar algún mensaje de error?
saschoar
Lo que podría haberse perdido se explica en esta respuesta SO: stackoverflow.com/a/6357957/1478093 -getListView().setOnScrollListener(this)
TBieniek
77
nota: agregar y eliminar el pie de página en la vista de lista es problemático (el pie de página no aparece si se llama a setAdapter antes de agregar el pie de página), terminé modificando la visibilidad de la vista del pie de página directamente sin eliminarla.
DVD
¿Qué se debe agregar en loadElements (currentPage) ??? Quiero decir, ¿llamo a mi AsyncTask o un hilo?
android_developer
20

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 )

Dariusz Bacinski
fuente
77
Es una mala práctica hacer otras cosas que no sean devolver una vista getView. Por ejemplo, no está garantizado que possea ​​visto por el usuario. Podría ser simplemente cosas de medición ListView.
Thomas Ahle
Quiero cargar más elementos después de desplazar 10 elementos. pero noto que la carga comienza antes de que el décimo elemento sea visible significa en el noveno elemento. Entonces, ¿cómo detener esto?
Atul Bhardwaj
4

¡ Ognyan Bankov GitHubEncontré una solución simple y funcional!

Utiliza el Volley HTTP libraryque 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:

  1. ListView que se rellena con solicitudes paginadas HTTP.
  2. Uso de NetworkImageView.
  3. Paginación ListView "sin fin" con lectura anticipada.

Para una consistencia futura, bifurqué el repositorio de Bankov .

oikonomopo
fuente
2

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.

Fernando Camargo
fuente
1

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 Footery 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.

tehcpu
fuente
0

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 OnScrollListenerimplementación no es suficiente, porque a veces los elementos no llenan la pantalla.

He escrito un paquete personal que admite una lista interminable RecyclerViewy también proporciona una implementación de cargador asíncrono AutoPagerFragmentque hace que sea muy fácil obtener datos de una fuente de varias páginas. Puede cargar cualquier página que desee en un RecyclerViewevento personalizado, no solo la página siguiente.

Aquí está la dirección: https://github.com/SphiaTower/AutoPagerRecyclerManager

ProtossShuttle
fuente
El enlace está roto.
DSlomer64
0

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.

Shubham Chaudhary
fuente
0

He estado trabajando en otra solución muy similar a esa, pero estoy usando a footerViewpara dar la posibilidad al usuario de descargar más elementos haciendo clic en footerView, estoy usando un "menú" que se muestra arriba ListViewy en la parte inferior de vista principal, este "menú" oculta la parte inferior del ListView, por lo tanto, cuando listViewse 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 footerViewse muestra en ese caso, el menú no aparece y el usuario puede ver footerViewpara 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) {

    }
});
pabloverd
fuente
0

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 getViewmé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 loadMoreelemento 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.

gbenroscience
fuente