web-dev-qa-db-fra.com

Comment paginer Firestore avec Android?

J'ai lu la documentation Firestore et tous les articles sur Internet (stackoverflow) sur la pagination Firestore mais pas de chance. J'ai essayé d'implémenter le code exact dans les documents, mais rien ne se passe. J'ai une base de données de base avec des articles (plus de 1250 ou plus) et je veux les obtenir progressivement. En faisant défiler pour charger 15 éléments (jusqu'au dernier élément de la base de données).

Si vous utilisez du code docs:

// Construct query for first 25 cities, ordered by population
Query first = db.collection("cities")
    .orderBy("population")
    .limit(25);

first.get()
    .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
    @Override
    public void onSuccess(QuerySnapshot documentSnapshots) {
        // ...

        // Get the last visible document
        DocumentSnapshot lastVisible = documentSnapshots.getDocuments()
            .get(documentSnapshots.size() -1);

        // Construct a new query starting at this document,
        // get the next 25 cities.
        Query next = db.collection("cities")
            .orderBy("population")
            .startAfter(lastVisible)
            .limit(25);

        // Use the query for pagination
        // ...
    }
});

Comment faire? La documentation n'a pas trop de détails.

PS: J'ai besoin de la vue recycleur (pas de la liste) lorsque l'utilisateur fait défiler. Merci

15
Johans Bormman

Comme il est mentionné dans la documentation officielle , la clé pour résoudre ce problème est d'utiliser la méthode startAfter () . Vous pouvez donc paginer les requêtes en combinant les curseurs de requête avec la méthode limit(). Vous pourrez utiliser le dernier document d'un lot comme début de curseur pour le prochain lot.

Pour résoudre ce problème de pagination, veuillez voir ma réponse à partir de ceci post, dans lequel j'ai expliqué étape par étape, comment vous peut charger des données à partir d'une base de données Cloud Firestore en plus petits morceaux et les afficher dans un ListView sur clic de bouton.

Solution:

Pour obtenir les données de votre base de données Firestore et les afficher en petits morceaux dans un RecyclerView, veuillez suivre les étapes ci-dessous.

Prenons l'exemple ci-dessus dans lequel j'ai utilisé des produits. Vous pouvez utiliser des produits, des villes ou tout ce que vous voulez. Les principes sont les mêmes. En supposant que vous souhaitez charger plus de produits lorsque l'utilisateur défile, j'utiliserai RecyclerView.OnScrollListener.

Définissons d'abord le RecyclerView, définissons le gestionnaire de disposition sur LinearLayoutManager et créons une liste. Nous instancions également l'adaptateur à l'aide de la liste vide et définissons l'adaptateur sur notre RecyclerView:

RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
List<ProductModel> list = new ArrayList<>();
ProductAdapter productAdapter = new ProductAdapter(list);
recyclerView.setAdapter(productAdapter);

Supposons que nous ayons une structure de base de données qui ressemble à ceci:

Firestore-root
   |
   --- products (collection)
         |
         --- productId (document)
                |
                --- productName: "Product Name"

Et une classe modèle qui ressemble à ceci:

public class ProductModel {
    private String productName;

    public ProductModel() {}

    public ProductModel(String productName) {this.productName = productName;}

    public String getProductName() {return productName;}
}

Voici à quoi devrait ressembler la classe d'adaptateur:

private class ProductAdapter extends RecyclerView.Adapter<ProductViewHolder> {
    private List<ProductModel> list;

    ProductAdapter(List<ProductModel> list) {
        this.list = list;
    }

    @NonNull
    @Override
    public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_product, parent, false);
        return new ProductViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ProductViewHolder productViewHolder, int position) {
        String productName = list.get(position).getProductName();
        productViewHolder.setProductName(productName);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }
}

La disposition item_product Contient une seule vue, une TextView.

<TextView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:id="@+id/text_view"
    Android:textSize="25sp"/>

Et voici à quoi devrait ressembler la classe titulaire:

private class ProductViewHolder extends RecyclerView.ViewHolder {
    private View view;

    ProductViewHolder(View itemView) {
        super(itemView);
        view = itemView;
    }

    void setProductName(String productName) {
        TextView textView = view.findViewById(R.id.text_view);
        textView.setText(productName);
    }
}

Définissons maintenant une limite en tant que variable globale et définissons-la sur 15.

private int limit = 15;

Définissons maintenant la requête en utilisant cette limite:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference productsRef = rootRef.collection("products");
Query query = productsRef.orderBy("productName", Query.Direction.ASCENDING).limit(limit);

Voici le code qui fait aussi la magie dans votre cas:

query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            for (DocumentSnapshot document : task.getResult()) {
                ProductModel productModel = document.toObject(ProductModel.class);
                list.add(productModel);
            }
            productAdapter.notifyDataSetChanged();
            lastVisible = task.getResult().getDocuments().get(task.getResult().size() - 1);

            RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                        isScrolling = true;
                    }
                }

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);

                    LinearLayoutManager linearLayoutManager = ((LinearLayoutManager) recyclerView.getLayoutManager());
                    int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
                    int visibleItemCount = linearLayoutManager.getChildCount();
                    int totalItemCount = linearLayoutManager.getItemCount();

                    if (isScrolling && (firstVisibleItemPosition + visibleItemCount == totalItemCount) && !isLastItemReached) {
                        isScrolling = false;
                        Query nextQuery = productsRef.orderBy("productName", Query.Direction.ASCENDING).startAfter(lastVisible).limit(limit);
                        nextQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                            @Override
                            public void onComplete(@NonNull Task<QuerySnapshot> t) {
                                if (t.isSuccessful()) {
                                    for (DocumentSnapshot d : t.getResult()) {
                                        ProductModel productModel = d.toObject(ProductModel.class);
                                        list.add(productModel);
                                    }
                                    productAdapter.notifyDataSetChanged();
                                    lastVisible = t.getResult().getDocuments().get(t.getResult().size() - 1);

                                    if (t.getResult().size() < limit) {
                                        isLastItemReached = true;
                                    }
                                }
                            }
                        });
                    }
                }
            };
            recyclerView.addOnScrollListener(onScrollListener);
        }
    }
});

Dans lequel lastVisible est un objet DocumentSnapshot qui représente le dernier élément visible de la requête. Dans ce cas, tous les 15 'et il est déclaré comme variable gloabl:

private DocumentSnapshot lastVisible;

Et isScrolling et isLastItemReached sont également des variables globales et sont déclarées comme:

private boolean isScrolling = false;
private boolean isLastItemReached = false;

Si vous voulez obtenir des données en temps réel, au lieu d'utiliser un appel à get(), vous devez utiliser addSnapshotListener() comme expliqué dans la documentation officielle concernant écouter plusieurs documents dans un collection .

23
Alex Mamo

FirebaseUI-Android a également récemment lancé un Paginator Firestore.

Je l'ai utilisé dans mon code, et cela fonctionne très bien - gardez à l'esprit qu'il fonctionne en utilisant .get () au lieu de .addSnapshotListener (), donc le recycleur n'est pas en temps réel.

Voir les documents ici:

https://github.com/firebase/FirebaseUI-Android/tree/master/firestore#using-the-firestorepagingadapter

5
Jeff Padgett