Comment puis-je créer une liste où lorsque vous atteignez la fin de la liste, je suis averti afin que je puisse charger plus d'articles?
Comment puis-je créer une liste où lorsque vous atteignez la fin de la liste, je suis averti afin que je puisse charger plus d'articles?
Réponses:
Une solution consiste à implémenter un OnScrollListener
et à apporter des modifications (comme l'ajout d'éléments, etc.) ListAdapter
à un état pratique dans sa onScroll
méthode.
Ce qui suit ListActivity
montre une liste d'entiers, 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 séparés pour les actions de longue durée (comme le chargement de données Web) et vous pouvez indiquer la progression dans le dernier élément de la liste (comme le font les applications de marché ou gmail).
firstVisible + visibleCount >= totalCount
est remplie plusieurs fois avec plusieurs appels à l'écouteur. Si la fonction de chargement supplémentaire est une demande Web (très probablement), ajoutez une autre vérification pour savoir si une demande est en cours ou non. D'un autre côté, vérifiez si le totalCount n'est pas égal au nombre total précédent, car nous n'avons pas besoin de plusieurs demandes pour le même nombre d'éléments dans la liste.
Je voulais juste apporter une solution que j'ai utilisée pour mon application.
Il est également basé sur l' OnScrollListener
interface, mais j'ai trouvé qu'il avait de bien meilleures performances de défilement sur les appareils bas de gamme, car aucun des calculs de nombre total / visible n'est effectué pendant les opérations de défilement.
ListFragment
ou ListActivity
mettre en œuvreOnScrollListener
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 doit être ajoutée à votre liste, et threshold
est le nombre d'éléments de liste (comptés à partir de la fin) qui devraient, s'ils sont visibles, déclencher le processus de chargement. Si vous définissez threshold
à 0
, par exemple, l'utilisateur doit faire défiler jusqu'à la fin de la liste afin de charger plus d' articles.
(facultatif) Comme vous pouvez le voir, la «vérification de chargement supplémentaire» n'est appelée que lorsque l'utilisateur arrête de faire défiler. 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 cet indicateur de chargement en appelant listView.removeFooterView(yourFooterView)
s'il n'y a plus d'éléments ou de pages.
ListFragment
également sans aucun problème - suivez simplement chacune des étapes ci-dessus et tout ira bien;) Ou pourriez-vous fournir des messages d'erreur?
getListView().setOnScrollListener(this)
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 le faire (à 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, veuillez appeler notifyDataSetChanged () dans votre adaptateur pour mettre à jour la vue (elle doit être exécutée sur le thread d'interface utilisateur - runOnUiThread )
getView
. Par exemple, vous n'êtes pas assuré que pos
l'utilisateur le voit. Il pourrait simplement s'agir de mesures ListView.
Chez Ognyan Bankov GitHub
j'ai trouvé une solution simple et fonctionnelle!
Il utilise le Volley HTTP library
qui rend la mise en réseau pour les applications Android plus facile et, surtout, plus rapide. Volley est disponible via le référentiel AOSP ouvert.
Le code donné montre:
Pour une cohérence future, j'ai bifurqué le repo de Bankov .
Voici une solution qui facilite également l'affichage d'une vue de chargement à la fin de ListView pendant le 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/ListViewWithLoadingIndicatorHelper.java - Aide à rendre possible l'utilisation du fonctionnalités sans s'étendre à partir de SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - Écouteur qui démarre le chargement des données lorsque l'utilisateur est sur le point d'atteindre le bas du ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Vous pouvez utiliser cette classe directement ou l'étendre.
Peut être un peu en retard mais la solution suivante s'est avérée très utile dans mon cas. D'une manière tout ce que vous devez faire est d'ajouter à votre ListView Footer
et de créer pour celaaddOnLayoutChangeListener
.
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 comme un charme.
La clé de ce problème est de détecter l'événement de chargement supplémentaire, de démarrer une demande asynchrone de données, puis de mettre à jour la liste. Un adaptateur avec indicateur de chargement et autres décorateurs est également nécessaire. En fait, le problème est très compliqué dans certains cas d'angle. Une simple OnScrollListener
mise en œuvre 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 sans fin RecyclerView
et fournit également une implémentation de chargeur asynchrone AutoPagerFragment
qui facilite très facilement l'obtention de données à partir d'une source de plusieurs pages. Il peut charger n'importe quelle page de votre choix dans un RecyclerView
événement personnalisé, pas seulement la page suivante.
Voici l'adresse: https://github.com/SphiaTower/AutoPagerRecyclerManager
Jusqu'à présent, la meilleure solution que j'ai vue est dans la bibliothèque FastAdapter pour les vues de recycleur. Il a un 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 sans hésiter.
J'ai travaillé dans une autre solution très similaire à celle-ci, mais, j'utilise un footerView
pour donner la possibilité à l'utilisateur de télécharger plus d'éléments en cliquant sur le footerView
, j'utilise un "menu" qui est affiché au-dessus du ListView
et en bas du vue parent, ce "menu" masque le bas du ListView
, donc, lorsque le listView
défilement disparaît et que l'état de défilement est inactif, le menu réapparaît, mais lorsque l'utilisateur défile jusqu'à la fin du listView
, je "demande" à savoir si footerView
s'affiche dans ce cas, le menu n'apparaît pas et l'utilisateur peut voir 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 le monde Android est principalement passé à RecyclerViews, mais pour toute personne intéressée, cette bibliothèque peut être très intéressante.
Il utilise le BaseAdapter utilisé avec le ListView pour détecter quand la liste a été défilée jusqu'au dernier élément ou lorsqu'elle est éloignée 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 Boy 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);
}
Remarquez comment la méthode getView de BoysAdapter renvoie un appel à la méthode de la superclasse EndlessAdapter getView
. C'est 100% essentiel.
Maintenant, pour créer l'adaptateur, procédez comme suit:
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);
}
}
};
le loadMore
élément est un bouton ou un autre élément d'interface utilisateur qui peut être cliqué pour extraire plus 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 xml et placez-le comme indiqué dans le code adaptateur ci-dessus.
Prendre plaisir.