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
.
FirebaseUI-Android también recientemente lanzó un Paginator de Firestore.
Lo he usado en mi código y funciona muy bien; solo tenga en cuenta que funciona con .get () en lugar de .addSnapshotListener (), por lo que el reciclador no está en tiempo real.
Vea los documentos aquí: