如何使用 Android 对 Firestore 进行分页? [英] How to paginate Firestore with Android?

查看:30
本文介绍了如何使用 Android 对 Firestore 进行分页?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我阅读了 Firestore 文档和互联网(stackoverflow)上关于 Firestore 分页的所有文章,但没有运气.我试图在文档中实现确切的代码,但没有任何反应.我有一个包含项目(超过 1250 个或更多)的基本数据库,我想逐步获取它们.通过滚动加载 15 个项目(到数据库中的最后一个项目).

I read Firestore documentation and all articles on internet(stackoverflow) about Firestore pagination but no luck. I tried to implement the exact code in docs, but nothing happens. I have a basic database with items(over 1250 or more) and I want to get them progressively. By scrolling to load 15 items (to the last item in the database).

如果使用文档代码:

// 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
        // ...
    }
});

怎么办?文档没有太多细节.

How to do? Documentation has not too many details.

PS:当用户滚动时,我需要使用回收器视图(不是列表视图).谢谢

PS: I need with recycler view (not list view) when user scrolls. Thanks

推荐答案

正如 官方文档,解决这个问题的关键是使用startAfter() 方法.因此,您可以通过将查询游标与 limit() 方法组合来对查询进行分页.您将能够使用批次中的最后一个文档作为下一个批次的光标开始.

As it is mentioned in the official documentation, the key for solving this problem is to use the startAfter() method. So you can paginate queries by combining query cursors with the limit() method. You'll be able to use the last document in a batch as the start of a cursor for the next batch.

要解决这个分页问题,​​请参阅我的回答 post,我在其中逐步解释了如何从 Cloud Firestore 数据库中以较小的块加载数据并将其显示在按钮单击时的 ListView.

To solve this pagination problem, please see my answer from this post, in which I have explained step by step, how you can load data from a Cloud Firestore database in smaller chunks and display it in a ListView on button click.

解决方案:

要从 Firestore 数据库中获取数据并在 RecyclerView 中以较小的块显示它,请按照以下步骤操作.

To get the data from your Firestore database and display it in smaller chunks in a RecyclerView, please follow the steps below.

让我们以我使用过的产品为例.您可以使用产品、城市或任何您想要的东西.原理是一样的.假设您想在用户滚动时加载更多产品,我将使用 RecyclerView.OnScrollListener.

Let's take the above example in which I have used products. You can use products, cities or whatever you want. The principles are the same. Assuming that you want to load more products when user scrolls, I'll use RecyclerView.OnScrollListener.

让我们首先定义RecyclerView,将布局管理器设置为LinearLayoutManager 并创建一个列表.我们还使用空列表实例化适配器并将适配器设置为我们的 RecyclerView:

Let's define first the RecyclerView, set the layout manager to LinearLayoutManager and create a list. We also instantiate the adapter using the empty list and set the adapter to our 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);

假设我们有一个如下所示的数据库结构:

Let's assume we have a database structure that looks like this:

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

还有一个看起来像这样的模型类:

And a model class that looks like this:

public class ProductModel {
    private String productName;

    public ProductModel() {}

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

    public String getProductName() {return productName;}
}

适配器类应该是这样的:

This how the adapter class should look like:

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();
    }
}

item_product 布局只包含一个视图,一个 TextView.

The item_product layout contains only one view, a TextView.

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

这是持有者类的样子:

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);
    }
}

现在,让我们将限制定义为全局变量并将其设置为 15.

Now, let's define a limit as a global variable and set it to 15.

private int limit = 15;

现在让我们使用此限制定义查询:

Let's define now the query using this limit:

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

这里的代码也对你的情况起到了作用:

Here is the code that also does the magic in your case:

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);
        }
    }
});

其中 lastVisible 是一个 DocumentSnapshot 对象,它代表查询中的最后一个可见项目.在这种情况下,每隔 15 个,它就被声明为一个全局变量:

In which lastVisible is a DocumentSnapshot object which represents the last visible item from the query. In this case, every 15'th one and it is declared as a global variable:

private DocumentSnapshot lastVisible;

isScrollingisLastItemReached 也是全局变量,声明为:

And isScrolling and isLastItemReached are also global variables and are declared as:

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

<小时>

如果您想实时获取数据,那么您需要使用 addSnapshotListener() 而不是使用 get() 调用,如关于 addSnapshotListener() 的官方文档中所述a href="https://firebase.google.com/docs/firestore/query-data/listen#listen_to_multiple_documents_in_a_collection" rel="noreferrer">收听集合中的多个文档.您可以在以下文章中找到更多信息:


If you want to get data in realtime, then instead of using a get() call you need to use addSnapshotListener() as explained in the official documentation regarding listening to multiple documents in a collection. More information you can find the following article:

这篇关于如何使用 Android 对 Firestore 进行分页?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆