web-dev-qa-db-fra.com

Liste infinie Android

Comment créer une liste où, lorsque vous arrivez à la fin de la liste, je suis averti afin de pouvoir charger plus d'éléments? 

228
Isaac Waller

Une solution consiste à implémenter un OnScrollListener et à apporter des modifications (comme l’ajout d’éléments, etc.) au ListAdapter à un état approprié dans sa méthode onScroll.

La ListActivity suivante montre une liste d’entiers, en commençant par 40, ajoutant des éléments lorsque l’utilisateur fait défiler jusqu’à la fin de la liste.

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

Vous devez évidemment utiliser des threads distincts pour les actions à exécution longue (comme le chargement de données Web) et vouloir indiquer la progression dans le dernier élément de la liste (comme le font les applications du marché ou gmail).

268
Josef Pfleger

Je voulais juste apporter une solution que j'ai utilisée pour mon application.

Il est également basé sur l’interface OnScrollListener, mais j’ai constaté que les performances de défilement sur les périphériques bas de gamme étaient bien meilleures, puisqu’aucun calcul de comptage visible/total n’est effectué pendant les opérations de défilement.

  1. Laissez votre ListFragment ou ListActivity implémenter OnScrollListener
  2. Ajoutez les méthodes suivantes à cette classe:

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

    currentPage est la page de votre source de données qui devrait être ajoutée à votre liste, et threshold est le nombre d'éléments de la liste (comptés à partir de la fin) qui devraient, s'ils sont visibles, déclencher le processus de chargement. Si vous définissez threshold sur 0, par exemple, l'utilisateur doit faire défiler l'écran jusqu'à la fin de la liste afin de charger plus d'éléments.

  3. (facultatif) Comme vous pouvez le constater, le "contrôle de chargement supplémentaire" n'est appelé que lorsque l'utilisateur arrête de faire défiler le texte. Pour améliorer la convivialité, vous pouvez gonfler et ajouter un indicateur de chargement à la fin de la liste via listView.addFooterView(yourFooterView). Un exemple pour une telle vue de pied de page:

    <?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. (facultatif) Enfin, supprimez l'indicateur de chargement en appelant listView.removeFooterView(yourFooterView) s'il n'y a plus d'éléments ou de pages.

93
saschoar

Vous pouvez détecter la fin de la liste à l'aide de onScrollListener , le code de travail est présenté ci-dessous:

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

Une autre façon de faire cela (à l'intérieur de l'adaptateur) est la suivante:

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

Sachez que cette méthode sera appelée plusieurs fois. Vous devez donc ajouter une autre condition pour bloquer plusieurs appels de addMoreData().

Lorsque vous ajoutez tous les éléments à la liste, appelez notifyDataSetChanged () à l'intérieur de votre adaptateur pour mettre à jour la vue (celle-ci doit être exécutée sur le fil de l'interface utilisateur - runOnUiThread ).

20
Dariusz Bacinski

Au Ognyan Bankov GitHub j’ai trouvé une solution simple et efficace! 

Il utilise le Volley HTTP library qui facilite la mise en réseau des applications Android et, surtout, plus rapidement. Volley est disponible dans le référentiel AOSP ouvert.

Le code donné montre:

  1. ListView qui est rempli par les demandes paginées HTTP.
  2. Utilisation de NetworkImageView.
  3. "Interminable" pagination ListView avec lecture anticipée.

Pour une future cohérence i a fourché le rapport de Bankov .

4
oikonomopo

Voici une solution qui facilite également l'affichage d'une vue de chargement à la fin de ListView pendant son chargement.

Vous pouvez voir les cours ici:

_ { https://github.com/CyberEagle/OpenProjects/blob/master/Android-projects/widgets/src/main/Java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingWithLoadingIndicatorHelper.Java - Aide permettant de pouvoir utiliser les fonctionnalités sans sortir de SimpleListViewWithLoadingIndicator.

_ { https://github.com/CyberEagle/OpenProjects/blob/master/Android-projects/widgets/src/main/Java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.Java - Listener qui commence à charger les données lorsque l'utilisateur est sur le point d'atteindre le bas de la liste.

_ { https://github.com/CyberEagle/OpenProjects/blob/master/Android-projects/widgets/src/main/Java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.Java - Le EndlessListView. Vous pouvez utiliser cette classe directement ou vous en étendre.

2
Fernando Camargo

Peut-être un peu tard, mais la solution suivante est très utile dans mon cas… .. D'une manière tout ce que vous avez à faire est d’ajouter à votre ListView une Footer et de créer pour cela addOnLayoutChangeListener.

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

Par exemple:

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

Cet événement se déclenche une fois lorsqu'un pied de page devient visible et fonctionne à merveille.

1
tehcpu

La meilleure solution que j'ai vue jusqu'à présent est celle de FastAdapter library pour les vues de recycleur. Il a EndlessRecyclerOnScrollListener

Voici un exemple d'utilisation: EndlessScrollListActivity

Une fois que je l'ai utilisé pour une liste de défilement sans fin, j'ai réalisé que la configuration est très robuste. Je le recommanderais certainement.

0

Je travaille dans une autre solution très similaire à celle-ci, mais, j’utilise une variable footerView pour permettre à l’utilisateur de télécharger plus d’éléments en cliquant sur la footerView, j’utilise un "menu" qui est affiché au-dessus de la ListView et dans le En bas de la vue parente, ce "menu" masque le bas de la ListView; ainsi, lorsque la listView défile le menu disparaît et lorsque le défilement est inactif, le menu réapparaît, mais lorsque l'utilisateur fait défiler jusqu'à la fin de la listView , Je "demande" à savoir si la footerView est affichée dans ce cas, le menu n'apparaît pas et l'utilisateur peut voir la footerView pour charger plus de contenu. Voici le code:

Cordialement.

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

    }
});
0
pabloverd

Je sais que c’est une vieille question et que le monde Android est principalement passé à RecyclerViews, mais pour toute personne intéressée, vous pouvez trouver la bibliothèque this très intéressante.

Il utilise l'adaptateur de base utilisé avec ListView pour détecter le défilement de la liste jusqu'au dernier élément ou le défilement du dernier élément.

Il est livré avec un exemple de projet (à peine 100 lignes de code d'activité) qui peut être utilisé pour comprendre rapidement comment cela fonctionne.

Utilisation simple:

class Boy{

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

}

Un adaptateur pour contenir des objets garçon ressemblerait à ceci:


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

}

Notez que la méthode getView de BoysAdapter renvoie un appel à la méthode getView de la superclasse EndlessAdapter. C'est 100% essentiel.

Maintenant, pour créer l'adaptateur, faites:

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

L'élément loadMore est un bouton ou un autre élément de l'interface utilisateur sur lequel il est possible de cliquer pour extraire davantage de données de l'URL. Lorsqu'il est placé comme décrit dans le code, l'adaptateur sait exactement quand afficher ce bouton et quand le désactiver. Créez simplement le bouton dans votre fichier XML et placez-le comme indiqué dans le code de l'adaptateur ci-dessus.

Prendre plaisir.

0
gbenroscience

La clé de ce problème consiste à détecter l'événement load-more, à lancer une demande asynchrone de données, puis à mettre à jour la liste. Un adaptateur avec indicateur de charge et autres décorateurs est également nécessaire. En fait, le problème est très compliqué dans certains cas. Une implémentation OnScrollListener ne suffit pas, car parfois les éléments ne remplissent pas l'écran.

J'ai écrit un package personnel qui prend en charge une liste infinie de RecyclerView et fournit également une implémentation de chargeur asynchrone AutoPagerFragment qui permet d'obtenir très facilement des données à partir d'une source multi-pages. Il peut charger n'importe quelle page de votre choix dans une RecyclerView lors d'un événement personnalisé, pas seulement la page suivante.

Voici l'adresse: https://github.com/SphiaTower/AutoPagerRecyclerManager

0
ProtossShuttle