java android firebase google-cloud-firestore

java - ¿Cómo paginar Firestore con Android?



firebase google-cloud-firestore (2)

Leí la documentación de Firestore y todos los artículos en Internet (stackoverflow) sobre la paginación de Firestore, pero no tuve suerte. Intenté implementar el código exacto en los documentos, pero no pasa nada. Tengo una base de datos básica con elementos (más de 1250 o más) y quiero obtenerlos progresivamente. Al desplazarse para cargar 15 elementos (hasta el último elemento de la base de datos).

Si está utilizando el código de documentos:

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

¿Cómo hacer? La documentación no tiene demasiados detalles.

PD: Necesito con la vista de reciclador (no la vista de lista) cuando el usuario se desplaza. Gracias


Como se menciona en la documentación oficial , la clave para resolver este problema es usar el método startAfter() . Por lo tanto, puede paginar las consultas combinando los cursores de consulta con el método limit() . Podrá usar el último documento en un lote como el inicio de un cursor para el siguiente lote.

Para resolver este problema de paginación, vea mi respuesta en esta post , en la que expliqué, paso a paso, cómo puede cargar datos de una base de datos de Cloud Firestore en trozos más pequeños y mostrarlos en un botón ListView al hacer clic.

Solución:

Para obtener los datos de su base de datos Firestore y mostrarlos en fragmentos más pequeños en un RecyclerView , siga los pasos a continuación.

Tomemos el ejemplo anterior en el que he usado productos. Puedes usar productos, ciudades o lo que quieras. Los principios son los mismos. Suponiendo que desea cargar más productos cuando el usuario se desplace, RecyclerView.OnScrollListener .

Primero definamos RecyclerView , configuramos el administrador de diseño en LinearLayoutManager y creamos una lista. También creamos una instancia del adaptador utilizando la lista vacía y configuramos el adaptador a nuestro 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);

Supongamos que tenemos una estructura de base de datos que se ve así:

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

Y una clase modelo que se ve así:

public class ProductModel { private String productName; public ProductModel() {} public ProductModel(String productName) {this.productName = productName;} public String getProductName() {return productName;} }

Así es como debería verse la clase de adaptador:

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

El diseño item_product contiene solo una vista, un TextView .

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

Y este es el aspecto que debe tener la clase titular:

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

Ahora, definamos un límite como una variable global y configurémoslo en 15 .

private int limit = 15;

Definamos ahora la consulta usando este límite:

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

Aquí está el código que también hace la magia en su caso:

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

En el que lastVisible es un objeto DocumentSnapshot que representa el último elemento visible de la consulta. En este caso, cada 15 ''y se declara como variable global:

private DocumentSnapshot lastVisible;

Y isScrolling y isLastItemReached también son variables globales y se declaran como:

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

Si desea obtener datos en tiempo real, en lugar de usar una llamada a get() , debe usar addSnapshotListener() como se explica en la documentación oficial sobre la escucha de varios documentos en una colección .