J'utilise un RecyclerView
et récupère des objets d'une API par lots de dix. Pour la pagination, j'utilise EndlessRecyclerOnScrollListener
.
Tout fonctionne correctement. Il ne reste maintenant qu’à ajouter un indicateur de progression au bas de la liste pendant que le prochain lot d’objets est récupéré par l’API. Voici une capture d'écran de l'application Google Play Store, montrant un ProgressBar
dans ce qui est sûrement un RecyclerView
:
Le problème est que ni RecyclerView
ni EndlessRecyclerOnScrollListener
ne disposent de la prise en charge intégrée permettant d’afficher un ProgressBar
en bas lors de l’extraction du prochain lot d’objets.
J'ai déjà vu les réponses suivantes:
1. Mettez un indéterminé ProgressBar
comme pied de page dans un RecyclerView
grille .
2. Ajout d'éléments à Endless Scroll RecyclerView
avec ProgressBar
en bas .
Je ne suis pas satisfait de ces réponses (toutes les deux par la même personne). Cela implique de placer un objet null
dans l’ensemble de données en cours de défilement, puis de le supprimer après la livraison du prochain lot. Cela ressemble à un hack qui évite le problème principal qui peut ou peut ne pas fonctionner correctement. Et cela provoque un peu de choc et de distorsion dans la liste
L'utilisation de SwipeRefreshLayout
n'est pas une solution ici. SwipeRefreshLayout
implique de tirer à partir du haut pour récupérer les éléments les plus récents, et il ne présente de toute façon pas une vue de progression.
Quelqu'un peut-il s'il vous plaît fournir une bonne solution pour cela? Je suis intéressé à savoir comment Google a implémenté cette fonctionnalité pour ses propres applications (l'application Gmail l'a également). Y at-il des articles où cela est montré en détail? Toutes les réponses et commentaires seront appréciés. Je vous remercie.
Quelques autres références :
1. Pagination avec RecyclerView
. (Superbe aperçu ...)
2. RecyclerView
en-tête et pied de page . (Plus de la même chose ...)
ICI IS UNE APPROCHE PLUS SIMPLE ET PROPRE.
Implémentez Endless Scrolling from this Codepath Guide , puis suivez les étapes suivantes.
1. Ajouter une barre de progression sous le RecyclerView.
<Android.support.v7.widget.RecyclerView
Android:id="@+id/rv_movie_grid"
Android:layout_width="0dp"
Android:layout_height="0dp"
Android:paddingBottom="50dp"
Android:clipToPadding="false"
Android:background="@Android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</Android.support.v7.widget.RecyclerView>
<ProgressBar
Android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:visibility="invisible"
Android:background="@Android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Ici Android: paddingBottom = "50dp" et Android: clipToPadding = "false" Sont très importants.
2. Obtenez une référence à la barre de progression.
progressBar = findViewById(R.id.progressBar);
3. Définissez les méthodes pour afficher et masquer la barre de progression.
void showProgressView() {
progressBar.setVisibility(View.VISIBLE);
}
void hideProgressView() {
progressBar.setVisibility(View.INVISIBLE);
}
J'ai implémenté ceci sur mon ancien projet, je l'ai fait comme suit ...
J'ai créé un interface
comme l'ont fait les gars de vos exemples
public interface LoadMoreItems {
void LoadItems();
}
Ensuite, j'ai ajouté une addOnScrollListener()
sur mon Adapter
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
//End of the items
if (onLoadMoreListener != null) {
onLoadMoreListener.LoadItems();
}
loading = true;
}
}
});
La onCreateViewHolder()
est l'endroit où je mets le ProgressBar
ou pas.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progressbar_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
Sur mon MainActivity
c'est là que j'ai mis la LoadItems()
pour ajouter les autres éléments:
mAdapter.setOnLoadMoreListener(new LoadMoreItems() {
@Override
public void LoadItems() {
DataItemsList.add(null);
mAdapter.notifyItemInserted(DataItemsList.size() - 1);
handler.postDelayed(new Runnable() {
@Override
public void run() {
// remove progress item
DataItemsList.remove(DataItemsList.size() - 1);
mAdapter.notifyItemRemoved(DataItemsList.size());
//add items one by one
//When you've added the items call the setLoaded()
mAdapter.setLoaded();
//if you put all of the items at once call
// mAdapter.notifyDataSetChanged();
}
}, 2000); //time 2 seconds
}
});
Pour plus d’informations, je viens de suivre Github repository
( Remarque: ceci utilise AsyncTask
peut-être est-il utile en tant que mon répondez, puisque je l’ai fait manuellement, pas avec les données de API
, mais cela devrait fonctionner aussi) aussi ce post m’a été utile sans fin -recyclerview-with-progress-bar
Aussi, je ne sais pas si vous l'avez nommé, mais j'ai aussi trouvé ce message infinite_scrolling_recyclerview , peut-être que cela pourrait aussi vous aider.
Si ce n'est pas ce que vous recherchez, faites le moi savoir et dites-moi ce qui ne va pas avec ce code et je vais essayer de le modifier comme vous le souhaitez.
J'espère que ça aide.
Puisque vous ne voulez pas supprimer un élément ... J'ai trouvé un gars qui supprime le footer
uniquement sur ce message: diseño-Android-endless-recyclerview .
Ceci est pour ListView
mais je sais que vous pouvez l’adapter à RecyclerView
il ne supprime aucun élément, il met juste Visible/Invisible
Le ProgressBar
jetez un oeil: - detecting-end-of-listview
Regardez aussi: cette question Android-implementing-progressbar-and-loading-for-endless-list-like-Android
Il y a une autre façon de faire cela.
listItems.size() + 1
VIEW_TYPE_LOADING
dans getItemViewType()
pour position >= listItems.size()
. De cette façon, le chargeur ne sera affiché qu'à la fin de la liste de vues du recycleur. Le seul problème avec cette solution est que, même après avoir atteint la dernière page, le chargeur sera affiché. Par conséquent, afin de résoudre le problème, vous stockez le x-pagination-total-count
Dans l'adaptateur, etalors vous changez la condition pour retourner le type de vue à
(position >= listItem.size())&&(listItem.size <= xPaginationTotalCount)
.
Je viens de proposer cette idée maintenant, qu'en pensez-vous?
voici ma solution de contournement sans ajouter de faux article (en Kotlin mais simple):
dans votre adaptateur, ajoutez:
private var isLoading = false
private val VIEWTYPE_FORECAST = 1
private val VIEWTYPE_PROGRESS = 2
override fun getItemCount(): Int {
if (isLoading)
return items.size + 1
else
return items.size
}
override fun getItemViewType(position: Int): Int {
if (position == items.size - 1 && isLoading)
return VIEWTYPE_PROGRESS
else
return VIEWTYPE_FORECAST
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEWTYPE_FORECAST)
return ForecastHolder(LayoutInflater.from(context).inflate(R.layout.item_forecast, parent, false))
else
return ProgressHolder(LayoutInflater.from(context).inflate(R.layout.item_progress, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
if (holder is ForecastHolder) {
//init your item
}
}
public fun showProgress() {
isLoading = true
}
public fun hideProgress() {
isLoading = false
}
maintenant, vous pouvez facilement appeler showProgress()
avant l'appel de l'API. et hideProgress()
après l'appel de l'API.
J'aime l'idée d'ajouter un détenteur de vue de progression à un adaptateur, mais cela a tendance à conduire à des manipulations logiques laides pour obtenir ce que vous voulez. L'approche du détenteur de la vue vous oblige à vous prémunir contre l'élément de pied de page supplémentaire en agitant les valeurs de retour de getItemCount()
, getItemViewType()
, getItemId(position)
et de tout type de getItem(position)
méthode que vous voudrez peut-être inclure.
Une autre approche consiste à gérer la visibilité de ProgressBar
au niveau Fragment
ou Activity
en affichant ou en masquant le ProgressBar
en dessous de RecyclerView
lorsque le chargement commence et se termine respectivement. Cela peut être réalisé en incluant le ProgressBar
directement dans la présentation de la vue ou en l'ajoutant à une classe personnalisée RecyclerView
ViewGroup
. Cette solution entraînera généralement moins de maintenance et moins de bugs.
UPDATE: Ma suggestion pose un problème lorsque vous faites défiler la vue vers le haut pendant le chargement du contenu. Le ProgressBar
restera au bas de la présentation. Ce n'est probablement pas le comportement que vous souhaitez. Pour cette raison, l'ajout d'un détenteur de vue de progression à votre adaptateur est probablement la meilleure solution fonctionnelle. Juste n'oubliez pas de garder vos méthodes d'accesseur d'article. :)
Cette solution est inspirée par la solution Akshar Patels sur cette page. Je l'ai modifié un peu.
Lors du chargement des premiers éléments, il semble agréable d’avoir centré la barre de progression.
Je n’aimais pas le remplissage vide restant en bas quand il n’y avait plus d’éléments à charger. Cela a été supprimé avec cette solution.
D'abord le XML:
<Android.support.v7.widget.RecyclerView Android:id="@+id/video_list" Android:paddingBottom="60dp" Android:clipToPadding="false" Android:layout_width="match_parent" Android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> </Android.support.v7.widget.RecyclerView> <ProgressBar Android:id="@+id/progressBar2" style="?android:attr/progressBarStyle" Android:layout_marginTop="10dp" Android:layout_width="wrap_content" Android:layout_centerHorizontal="true" Android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/>
Puis j'ai ajouté ce qui suit par programme.
Lorsque les premiers résultats ont été chargés, ajoutez ceci à votre onScrollListener. Il déplace la barre de progression du centre vers le bas:
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) loadingVideos.getLayoutParams();
layoutParams.topToTop = ConstraintLayout.LayoutParams.UNSET;
loadingVideos.setLayoutParams(layoutParams);
Quand il n'y a plus d'articles, supprimez le rembourrage en bas comme ceci:
recyclerView.setPadding(0,0,0,0);
Cachez et affichez votre barre de progression comme d'habitude.
Vous pouvez utiliser la balise layout_above dans recycleView comme ceci:
<RelativeLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_marginLeft="16dp"
Android:layout_marginRight="16dp"
Android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
Android:id="@+id/rv"
Android:layout_below="@+id/tv2"
Android:layout_width="match_parent"
Android:focusable="false"
Android:layout_height="wrap_content"
Android:layout_marginTop="24dp"
Android:layout_above="@+id/pb_pagination"/>
<ProgressBar
Android:id="@+id/pb_pagination"
style="@style/Widget.AppCompat.ProgressBar"
Android:layout_width="30dp"
Android:layout_height="30dp"
Android:indeterminate="true"
Android:visibility="gone"
Android:layout_alignParentBottom="true"
Android:layout_centerHorizontal="true" />
</RelativeLayout>