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?
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).
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.
ListFragment
ou ListActivity
implémenter OnScrollListener
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);
}
}
}
où 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.
(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>
(facultatif) Enfin, supprimez l'indicateur de chargement en appelant listView.removeFooterView(yourFooterView)
s'il n'y a plus d'éléments ou de pages.
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 ).
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:
Pour une future cohérence i a fourché le rapport de Bankov .
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.
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.
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.
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) {
}
});
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.
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