Firebase Android Pagination

Firebase android pagination

Below is the code I'm using for pagination which shows the latest node first.

      public void getImages() {
Query imagesQuery = FirebaseDatabase.getInstance().getReference().child("englishDps").child(mChildName).orderByKey().limitToLast(21);

ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Image image = dataSnapshot.getValue(Image.class);
image.setNodeKey(dataSnapshot.getKey());

mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}

@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {

}

@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {

}

@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {

}

@Override
public void onCancelled(DatabaseError databaseError) {
if (isAdded()) {
Toast.makeText(getActivity(), "Problem loading more images...", Toast.LENGTH_LONG).show();
}
}
};

imagesQuery.addChildEventListener(childEventListener);
}

@Override
public void getMoreImages() {
if (!mGettingMoreImages) {
mGettingMoreImages = true;
Query imagesQuery = FirebaseDatabase.getInstance().getReference("englishDps").child(mChildName).orderByKey().endAt(mLastKey).limitToLast(21);

ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Image image = dataSnapshot.getValue(Image.class);
image.setNodeKey(dataSnapshot.getKey());
mMoreImages.add(image);
if (mMoreImages.size() == 21) {
mLastKey = mMoreImages.get(0).getNodeKey();
Collections.reverse(mMoreImages);
mMoreImages.remove(mMoreImages.size() - 1);
mImages.addAll(mMoreImages);
mMoreImages.clear();
mGettingMoreImages = false;
mImagesAdapter.notifyDataSetChanged();
return;
}

if (mLastKey.equalsIgnoreCase(image.getNodeKey())) {
Collections.reverse(mMoreImages);
mImages.addAll(mMoreImages);
mMoreImages.clear();
mGettingMoreImages = false;
mImagesAdapter.onNoMoreImages();
;
mImagesAdapter.notifyDataSetChanged();
}
}

@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {

}

@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {

}

@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {

}

@Override
public void onCancelled(DatabaseError databaseError) {
if (isAdded()) {
Toast.makeText(getActivity(), "Problem loading more images...", Toast.LENGTH_LONG).show();
}
}
};

imagesQuery.addChildEventListener(childEventListener);
}
}

Pagination in Firebase realtime database

FirebaseDatabase.getInstance(firebaseApp)
.getReference("resources")
.child(FSeason.key)
.orderByKey()
.startAt(id)
.limit(size)

Here, startAt(...) will set the cursor in your database. The order of these data is defined by your orderByKey() function which orders your data alphabetically. Moreover, the limit(...) function defines how many data to retrieve per query.

Here's the official documentation on startAt(...):

Create a query constrained to only return child nodes with a value
greater than or equal to the given value, using the given orderBy
directive or priority as default.

The id in startAt(id) represents the id of the last retrieved document. So, every time you parse the results from your query, store the last element's id and use that for the next query.

Pagination in endless recycler view with firebase

To recognize that we have reached end of RecyclerView you can use this class EndlessRecyclerOnScrollListener.java

To load more next question, you should define one more field in Question class like number

public class Question {
private int number; // it must unique and auto increase when you add new question
...
}

Then when you load questions from FireBase you can do like

public class MainActivity extends AppCompatActivity {
private static final int TOTAL_ITEM_EACH_LOAD = 10;
private DatabaseReference mDatabase;
final List<Question> questionList = new ArrayList<>();

private int currentPage = 0;

private RecyclerView recyclerView;
private RecyclerViewAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
...
// init and set layout manager for your RecyclerView
...
mAdapter = new RecyclerViewAdapter(questionList);
recyclerView.setAdapter(mAdapter);
recyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager) {
@Override
public void onLoadMore(int current_page) { // when we have reached end of RecyclerView this event fired
loadMoreData();
}
});
loadData(); // load data here for first time launch app
}

private void loadData() {
// example
// at first load : currentPage = 0 -> we startAt(0 * 10 = 0)
// at second load (first loadmore) : currentPage = 1 -> we startAt(1 * 10 = 10)
mDatabase.child("questions")
.limitToFirst(TOTAL_ITEM_EACH_LOAD)
.startAt(currentPage*TOTAL_ITEM_EACH_LOAD)
.orderByChild("number")
.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.hasChildren()){
Toast.makeText(MainActivity.this, "No more questions", Toast.LENGTH_SHORT).show();
currentPage--;
}
for (DataSnapshot data : dataSnapshot.getChildren()) {
Question question = data.getValue(Question.class);
questionList.add(question);
mAdapter.notifyDataSetChanged();
}
}

@Override public void onCancelled(DatabaseError databaseError) {}});
}

private void loadMoreData(){
currentPage++;
loadData();
}
}

Here is my DEMO project

firebase android pagination with recyclerview

You'd use endAt(), since you want the last item to be returned to be the "oldest" one that you got before.

DatabaseReference jokesRef = FirebaseDatabase.getInstance().getReference().child("hindi-jokes");
Query jokesQuery = jokesRef.orderByKey().endAt(oldestKeyYouveSeen).limitToLast(20);

The oldestKeyYouveSeen is known as the anchor. You need to track this yourself in your code: setting it to the key of the oldest item that you'd you've seen.

Note that it will be both in the first query and in the second, so you'll have to explicitly exclude it once in your code.



Related Topics



Leave a reply



Submit