web-dev-qa-db-fra.com

Endless RecyclerView avec ProgressBar pour la pagination

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:

enter image description here

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

3. Sans fin RecyclerView avec ProgressBar en bas .

56
Y.S

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);
}
15
Akshar Patel

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.

MODIFIER

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

16
Skizo-ozᴉʞS

Il y a une autre façon de faire cela.

  • Tout d'abord, getItemCount de votre adaptateur renvoie listItems.size() + 1
  • retourne 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, et
  • alors 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?

5
Bhargav

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.

3
Amir Ziarati

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 RecyclerViewViewGroup. 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. :)

3
Ryan

Cette solution est inspirée par la solution Akshar Patels sur cette page. Je l'ai modifié un peu.

  1. Lors du chargement des premiers éléments, il semble agréable d’avoir centré la barre de progression.

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

0
John T

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>
0
Houssin Boulla