【问题标题】:Firestore PaginationFirestore 分页
【发布时间】:2018-10-26 07:15:27
【问题描述】:

我有一个关于如何使用 Firestore 正确分页查询的问题。

通过将下一个查询放入上一个查询的 OnSuccessListener 中,就像在 Firestore 页面上的示例中一样,它不会不可避免地总是触发一次加载所有页面的连锁反应吗?这不是我们想要通过分页来避免的吗?

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

来源:https://firebase.google.com/docs/firestore/query-data/query-cursors

这是我的方法。我知道在真正的应用程序中我应该使用RecyclerView,但我只想在TextView 上测试它。我想在每次单击按钮时加载 3 个文档。 将lastResult 存储为成员然后检查它是否不为空,看看它是否是第一个查询是否有意义?

public void loadMore(View v) {
    Query query;
    if (lastResult == null) {
        query = notebookRef.orderBy("priority")
                .orderBy("title")
                .limit(3);
    } else {
        query = notebookRef.orderBy("priority")
                .orderBy("title")
                .startAfter(lastResult)
                .limit(3);
    }
    query.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
            String data = "";

            for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
                Note note = documentSnapshot.toObject(Note.class);
                note.setDocumentId(documentSnapshot.getId());

                String documentId = note.getDocumentId();
                String title = note.getTitle();
                String description = note.getDescription();
                int priority = note.getPriority();

                data += "ID: " + documentId
                        + "\nTitle: " + title + "\nDescription: " + description
                        + "\nPriority: " + priority + "\n\n";
            }

            if (queryDocumentSnapshots.size() > 0) {
                data += "_____________\n\n";
                textViewData.append(data);


                lastResult = queryDocumentSnapshots.getDocuments()
                        .get(queryDocumentSnapshots.size() - 1);
            }
        }
    });
}

【问题讨论】:

  • 你解决了吗?
  • 有人告诉我我的方法很好
  • 它对你有用吗?
  • 是的,它正在工作
  • 你的问题有确切的代码吗?

标签: java android firebase google-cloud-firestore


【解决方案1】:

你必须在你的 recyclerview/listview 中使用 ScrollListener。开始时您将获取 25 个数据限制,一旦用户再次滚动到页面末尾,您必须使用限制进行新的 Firestore 调用(无论您保留什么)。但此时您必须在查询中继续使用startAt()startAt() 的输入将是您从第一个获取的数据中获取的最后一个键。它只是基本概述。 可以参考this链接查询。

您可以使用 firestore 在 recyclerview/listview 中创建分页,如下所示:

基本上遵循以下步骤:

1) 在打开 Activity/Fragment 时,您的第一个查询将获取 25 个数据限制

Query first = db.collection("cities")
        .orderBy("population")
        .limit(25);

first.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot documentSnapshots) {
            // add data to recyclerView/listview

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

2) 现在覆盖适配器的 onScrollListener

boolean isEndChildResults = false;
    mRecyclerView.addOnScrollListener(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);
            currentVisibleItem = linearLayoutManager.getChildCount();
            totalItem = linearLayoutManager.getItemCount();
            scrolledItem = linearLayoutManager.findFirstVisibleItemPosition();
            if (isScrolling && (currentVisibleItem + scrolledItem == totalItem) && !isEndChildResults && documentSnapshot != null) {
                isScrolling = false;
                mProgressBarScroll.setVisibility(View.VISIBLE);

                FirebaseFirestore firebaseFirestore = FirebaseFirestore.getInstance();

                Query query = firebaseFirestore.collection(...).document(...).limit(25).orderBy(...).startAt(lastVisible);
                query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {

                        if (task.isSuccessful()) {
                          // add data to recyclerView/listview
                          lastVisible = documentSnapshots.getDocuments().get(documentSnapshots.size() -1);


                     if (task.getResult().size() < postPerPageLimit) {
                       // if your result size is less than your query size which means all the result has been displayed and there is no any other data to display 
                                    isEndChildResults = true;
                                }
                            }

                        }
                    }
                });

          if(isEndChildResults){
         // show snackbar/toast
            }
         }

*lastVisible documentSnapshot 在每次滚动时都会发生变化,它会从 lastVisible 快照中获取数据

【讨论】:

  • 但是上面的例子是在第一个查询的 OnSuccessListener 中检索下一个查询,所以在这个匿名类之外是不可用的。
  • @FlorianWalther 我已经更新了我的答案,如果您仍有任何疑问,请告诉我。
  • 谢谢,但是lastVisible 必须是成员变量,对吧?因为现在它是在第一个 OnSuccessListener 的范围内声明的
  • @FlorianWalther 是的,它必须是成员变量。
  • @RohitMaurya 也许你也可以看看this。谢谢!
【解决方案2】:

构造查询尚未从该查询中读取数据。

所以这段代码只是创建了一个查询:

Query next = db.collection("cities")
        .orderBy("population")
        .startAfter(lastVisible)
        .limit(25);

它不从数据库中读取任何数据。这意味着它也不会触发任何onSuccess 方法。

如果您立即next.get().addOnSuccessListener(...,您确实会创建一个加载所有页面的循环。

【讨论】:

  • 但是查询变量是在匿名内部类中声明的,所以它不能从外部访问,对吧?那我以后怎么用呢?
  • 这只是一个如何构造这样一个查询的例子。
  • 那么在一个真实的例子中,我们会将下一个查询存储在一个成员变量中?
  • 那一种方式。或者只是将lastVisible 保留在成员中,并在需要时构造查询。
  • 是的,我的意思是 lastVisible,抱歉。如果还没有查询,这个变量将为空,所以如果我希望同一个按钮进行第一次加载以及所有后续加载,我会进行空检查并根据结果创建 2 个不同的查询?一个带有 startAfter 的查询,一个没有。
猜你喜欢
  • 2019-01-13
  • 2019-05-29
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
  • 2018-06-11
  • 2020-05-28
  • 2020-08-23
  • 1970-01-01
相关资源
最近更新 更多