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

Java 如何使用Android对Firestore进行分页?,java,android,firebase,google-cloud-firestore,Java,Android,Firebase,Google Cloud Firestore,我阅读了Firestore文档和互联网上所有关于Firestore分页的文章(stackoverflow),但运气不好。我试图在文档中实现精确的代码,但什么也没发生。我有一个包含项目(超过1250个或更多)的基本数据库,我想逐步获取它们。通过滚动加载15项(到数据库中的最后一项) 如果使用文档代码: // Construct query for first 25 cities, ordered by population Query first = db.collection("cities")

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

如果使用文档代码:

// 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
        // ...
    }
});
//按人口顺序构建前25个城市的查询
Query first=db.collection(“城市”)
.orderBy(“总体”)
.限额(25);
首先,获取()
.addOnSuccessListener(新的OnSuccessListener(){
@凌驾
成功时公共无效(QuerySnapshot文档快照){
// ...
//获取最后一个可见文档
DocumentSnapshot lastVisible=documentSnapshots.getDocuments()
.get(documentSnapshots.size()-1);
//从该文档开始构造一个新查询,
//获取接下来的25个城市。
Query next=db.collection(“城市”)
.orderBy(“总体”)
.startAfter(最后可见)
.限额(25);
//使用查询进行分页
// ...
}
});
怎么办?文档中没有太多的细节

PS:当用户滚动时,我需要使用recycler视图(而不是列表视图)。感谢中提到的,解决此问题的关键是使用此方法。因此,您可以通过将查询游标与
limit()
方法相结合来对查询进行分页。您将能够使用批处理中的最后一个文档作为下一批处理的光标的开始

要解决此分页问题,请参阅我在此中的答案,我在其中逐步解释了如何从Cloud Firestore数据库中以较小的块加载数据,并在单击按钮时将其显示在
列表视图中

解决方案:

要从Firestore数据库获取数据并在
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);
还有一个模型类,如下所示:

Firestore-root
   |
   --- products (collection)
         |
         --- productId (document)
                |
                --- productName: "Product Name"
public class ProductModel {
    private String productName;

    public ProductModel() {}

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

    public String getProductName() {return productName;}
}
以下是适配器类的外观:

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();
    }
}
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);
    }
}
holder类应该是这样的:

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

private int limit = 15;
现在让我们使用此限制定义查询:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference productsRef = rootRef.collection("products");
Query query = productsRef.orderBy("productName", Query.Direction.ASCENDING).limit(limit);
以下是在您的案例中发挥神奇作用的代码:

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);
        }
    }
});
IsCrolling
IsLastitemReach
也是全局变量,声明为:

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

如果要实时获取数据,则需要使用
addSnapshotListener()
调用,而不是使用
get()
调用,如官方文档中所述。有关更多信息,请参阅以下文章:


FirebaseUI Android最近也推出了Firestore Paginator

我在我的代码中使用过它,它工作得很好——请记住,它使用.get()而不是.addSnapshotListener()进行操作,因此回收器不是实时的

请参见此处的文档:


您还可以使用Firebase UI Firestore提供的
FirestorePagingAdapter

您需要安装此依赖项

实现'com.firebaseui:firebaseui firestore:latest\u version\u here'
解决方案 步骤1:创建全局Firestore分页适配器变量,并传递模型类和ViewHolder,以及模型变量

专用FirestorePagingAdapter适配器;
私有模型;
步骤2: 创建firebase查询

Query Query=db.collection(“城市”)
.orderBy(“人口”);
步骤3: 让我们构建页面列表配置。在这里,您将传递每个页面中要查询的数据量

PagedList.Config Config=new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.设置预取距离(10)
.setPageSize(15)
.build();
步骤4:设置配置后,现在让我们构建Firestore分页选项,您将在其中传递
查询
配置

FirestorePagingOptions选项=新建FirestorePagingOptions.Builder()
.setLifecycleOwner(此)
.setQuery(查询、配置、快照->{
model=snapshot.toObject(model.class);
收益模型;
})
.build();
步骤:5 现在,让我们将数据传递到Recylerview

adapter=新FirestorePagingAdapter(选项){
@凌驾
受保护的无效onBindViewHolder(@NonNull ModelViewHolder holder,int位置,@NonNull Model){
夹持器(型号);
}
@非空
@凌驾
public ModelViewHolder onCreateViewHolder(@NonNull ViewGroup父级,int-viewType){
View=LayoutFlater.from(parent.getContext()).flate(R.layout.layout_model,parent,false);
返回新的ModelViewHolder(视图);
}
@凌驾
受保护的void onError(@NonNull异常e){
super.onError(e);
//这里的逻辑
}
@凌驾
受保护的void onLoadingState已更改(@NonNull LoadingState状态){
开关(状态){
案例加载_首字母:
打破
案例加载\u更多: