Как указано в официальной документации , ключом к решению этой проблемы является использование метода startAfter () .Таким образом, вы можете разбивать запросы на страницы, комбинируя курсоры запросов с помощью метода limit()
.Вы сможете использовать последний документ в пакете в качестве начала курсора для следующего пакета.
Чтобы решить эту проблему нумерации страниц, см. Мой ответ из этой записи , в котором я объяснил шаг за шагом, как вы можете загружать данные из базы данных Cloud Firestore небольшими порциями и отображать их при нажатии кнопки ListView
.
Решение:
Чтобы получить данные из базы данных 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();
}
}
Макет item_product
содержит только одно представление, TextView
.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_view"
android:textSize="25sp"/>
И вот как должен выглядеть класс держателя:
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);
}
}
});
В котором lastVisible
- это DocumentSnapshot
объект, который представляет последний видимый элемент из запроса.В этом случае каждый 15-й и он объявляется как глобальная переменная:
private DocumentSnapshot lastVisible;
И isScrolling
и isLastItemReached
также являются глобальными переменными и объявляются как:
private boolean isScrolling = false;
private boolean isLastItemReached = false;
Если вы хотите получать данные в режиме реального времени, вместо звонка get()
вам нужно использовать addSnapshotListener()
, как объяснено в официальной документации относительно прослушивания нескольких документов в коллекции .