如何使用Android对Firestore进行分页?

Joh*_*man 15 java android firebase google-cloud-firestore

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

如果使用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
        // ...
    }
});
Run Code Online (Sandbox Code Playgroud)

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

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

Ale*_*amo 29

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

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

解:

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

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

让我们首先定义RecyclerView,设置布局管理器LinearLayoutManager并创建一个列表.我们还使用空列表实例化适配器并将适配器设置为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);
Run Code Online (Sandbox Code Playgroud)

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

Firestore-root
   |
   --- products (collection)
         |
         --- productId (document)
                |
                --- productName: "Product Name"
Run Code Online (Sandbox Code Playgroud)

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

public class ProductModel {
    private String productName;

    public ProductModel() {}

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

    public String getProductName() {return productName;}
}
Run Code Online (Sandbox Code Playgroud)

这个适配器类应该是这样的:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

item_product布局只包含一个观点,一个TextView.

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/text_view"
    android:textSize="25sp"/>
Run Code Online (Sandbox Code Playgroud)

这就是持有者类应该是这样的:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

private int limit = 15;
Run Code Online (Sandbox Code Playgroud)

现在让我们使用这个限制来定义查询:

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

以下是在您的案例中也具有魔力的代码:

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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

其中lastVisible一个DocumentSnapshot对象代表查询中的最后一个visibile项.在这种情况下,每15个一个,它被声明为gloabl变量:

private DocumentSnapshot lastVisible;
Run Code Online (Sandbox Code Playgroud)

isScrollingisLastItemReached也是全局变量,并声明如下:

private boolean isScrolling = false;
private boolean isLastItemReached = false;
Run Code Online (Sandbox Code Playgroud)

如果您想实时获取数据,那么get()您需要使用addSnapshotListener()正式文档中有关收听集合中多个文档的说明,而不是使用呼叫.

  • 正如我所说,记录显示在“ListView”中,但这只是一个示例。您可以使用“RecyclerView”实现相同的效果。你尝试过吗? (2认同)
  • @kontashi35我看不出你添加那篇文章的原因,因为它与我的答案完全一样:| (2认同)

Jef*_*ett 5

FirebaseUI-Android最近也推出了Firestore Paginator。

我已经在代码中使用了它,并且效果很好-请记住,它使用.get()而不是.addSnapshotListener()进行操作,因此回收站不是实时的。

在这里查看文档:

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