21

I'm building an app which will show videos stored on firebase. The list of videos needs to be paginated fetching most recent 20 videos at a time.

enter image description here

Here is the code I thought would work

  private void getVideos() {

        Query videosQuery = FirebaseUtil.getVideosRef();
        videosQuery.startAt(0);
        videosQuery.endAt(1);

        ChildEventListener videosChildEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                String date = dataSnapshot.getKey();
                String temp = date;
            }

            @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) {

                Log.d(tag, "database error");
            }
        };


        ValueEventListener videoValueEventListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                String date = dataSnapshot.getKey();
                String temp = date;

               long count =  dataSnapshot.getChildrenCount();
                String value = dataSnapshot.getValue().toString();
                temp = value;
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.d(tag, "database error");
            }
        };
//        videosQuery.addChildEventListener(videosChildEventListener);
        videosQuery.addValueEventListener(videoValueEventListener);

    }

But above code retrieves entire list of videos instead of limited videos. How can pagination be implemented.

Vihaan Verma
  • 12,815
  • 19
  • 97
  • 126

10 Answers10

18

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);
        }
    }
Vihaan Verma
  • 12,815
  • 19
  • 97
  • 126
  • COuld u please tell me getMoreImages() is your overrided method??? I also want to use the pagination concept like u used..Where is your onLoadMore functionality?? – Ravindra Kushwaha Mar 02 '17 at 13:03
  • fragment implements interface in my code. You can remove the @override notation – Vihaan Verma Mar 06 '17 at 21:30
  • Could you please explain what `.onNoMoreImages();` is? Thanks – Drew Szurko Mar 14 '17 at 05:10
  • Thanks for the above code, I finally found the solution I needed! The above `childEvenListener` was giving me duplicate `RecyclerView` items because they were not being removed every time I queried. So what I had to do differently was use `addListenerForSingleValueEvent` instead of `childEventListener`. That way the listener is immediately removed. Thanks again! – Drew Szurko Mar 14 '17 at 14:47
  • 2
    **According to Firebase documentation:** _You can use startAt(), endAt(), and equalTo() to choose arbitrary starting, ending, and equivalence points for queries. This can be useful for paginating data or finding items with children that have a specific value._ – mirzak Jun 03 '18 at 17:05
  • @KetanRamani have a listener in your adapter to decide when you want to start loading more images. Say at position items.size()-10 is when you make the network call – Vihaan Verma Mar 23 '19 at 13:41
  • I don't get it, what if you have 19? you never render anything? – desgraci Dec 23 '19 at 09:55
  • i got logic understanding from your answer but ddo it with some simple steps and make it simple – Adnan Bashir Apr 20 '22 at 06:47
14

I have following method to paginate through a firebase realtime database node:

private void getUsers(String nodeId) {
        Query query;

        if (nodeId == null)
            query = FirebaseDatabase.getInstance().getReference()
                    .child(Consts.FIREBASE_DATABASE_LOCATION_USERS)
                    .orderByKey()
                    .limitToFirst(mPostsPerPage);
        else
            query = FirebaseDatabase.getInstance().getReference()
                    .child(Consts.FIREBASE_DATABASE_LOCATION_USERS)
                    .orderByKey()
                    .startAt(nodeId)
                    .limitToFirst(mPostsPerPage);

        query.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                UserModel user;
                List<UserModel> userModels = new ArrayList<>();
                for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
                    userModels.add(userSnapshot.getValue(UserModel.class));
                }

                mAdapter.addAll(userModels);
                mIsLoading = false;
            }

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

Every time I reach the bottom of the paginated data, I call the getUsers(mAdapter.getLastItemId()); and then it brings the next set of records.

I have written a complete guide with open source sample app on this that you can check at https://blog.shajeelafzal.com/2017/12/13/firebase-realtime-database-pagination-guide-using-recyclerview/

Shajeel Afzal
  • 5,913
  • 6
  • 43
  • 70
  • *firebaser here* Great answer. Nowadays I'd use `startAfter` instead of `startAt` though, to prevent having a duplicate item between the pages. – Frank van Puffelen Sep 06 '22 at 13:26
4

You want to be using the limitToFirst/limitToLast methods to retrieve a limited number of results.

videosQuery.orderByKey().limitToFirst(20)

https://www.firebase.com/docs/web/api/query/limittofirst.html

You should really consider changing the naming convention of your videos to include leading 0s (i.e. video01, video02... video10, video11) because the above code will display them exactly as you have them above (which I assume is out of order?)

Alternatively, if you're adding the videos via Java, you could just let firebase create the uniqueids via push(). The uniqueid are generated in a way that they'll sort chronilogically, which sounds like it'll suit your need to grab the most recent(ly added?) videos.

https://www.firebase.com/docs/web/api/firebase/push.html

crookful
  • 57
  • 1
4
 mPageEndOffset = 0;
 mPageLimit = 10;


 mPageEndOffset += mPageLimit;

 deviceListQuery = mDatabase.child("users")
 .orderByChild("id").limitToFirst(mPageLimit).startAt(mPageEndOffset);

 deviceListQuery.addValueEventListener(YourActivity.this);
Jayakrishnan
  • 4,457
  • 29
  • 29
  • 1
    I am having an issue for getting it to work in reverse, any ideas? http://stackoverflow.com/questions/40231335/how-to-paginate-firebase-from-the-last-items-to-the-first – Lion789 Oct 25 '16 at 04:23
  • Add a field that has a descending value to the data, for ex: unix_timestamp (or inverted timestamp) and use limitToLast() with your select query. We dont really need pagination as firebase query execute and return fast. I use a query for getting 1000 records in single query not much delay experienced so far – Jayakrishnan Oct 25 '16 at 16:02
  • How would one attach a unix_timestamp (or inverted timestamp) ? Currently I'm adding a timestamp by the following method 'ServerValue.TIMESTAMP' – Zen Jan 02 '17 at 14:02
  • @JayakrishnanPm Well, wouldn't this force you to have an id field inside of the video child implementation? And the id needs to be an integer, which you have to increment on creation yourself? – JacksOnF1re Feb 01 '17 at 15:11
  • @JacksOnF1re if you want to sort by a field (integer field) then add a timestamp field it so you don't need to autoincrement it programmatically. – Jayakrishnan Feb 03 '17 at 11:37
  • @JayakrishnanPM java.lang.IllegalArgumentException: You must use startAt(String value), endAt(String value) or equalTo(String value) in combination with orderByKey(). Other type of values or using the version with 2 parameters is not supported – Mohamed Zakaria El-Zoghbi Sep 24 '17 at 20:21
1

You can achieve it by using Firebase database paging library as below code... In this code, I have shown Post as an data model item and PostViewHolder as ViewHolder

        //Initialize RecyclerView
        mRecyclerView = findViewById(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);

        LinearLayoutManager mManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mManager);

        //Initialize Database
        mDatabase = FirebaseDatabase.getInstance().getReference().child("posts");

        //Initialize PagedList Configuration
        PagedList.Config config = new PagedList.Config.Builder()
                .setEnablePlaceholders(false)
                .setPrefetchDistance(5)
                .setPageSize(10)
                .build();

        //Initialize FirebasePagingOptions
        DatabasePagingOptions<Post> options = new DatabasePagingOptions.Builder<Post>()
                .setLifecycleOwner(this)
                .setQuery(mDatabase, config, Post.class)
                .build();

        //Initialize Adapter
        mAdapter = new FirebaseRecyclerPagingAdapter<Post, PostViewHolder>(options) {
            @NonNull
            @Override
            public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                return new PostViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false));
            }

            @Override
            protected void onBindViewHolder(@NonNull PostViewHolder holder,
                                            int position,
                                            @NonNull Post model) {
                holder.setItem(model);
            }

            @Override
            protected void onLoadingStateChanged(@NonNull LoadingState state) {
                switch (state) {
                    case LOADING_INITIAL:
                    case LOADING_MORE:
                        // Do your loading animation
                        mSwipeRefreshLayout.setRefreshing(true);
                        break;

                    case LOADED:
                        // Stop Animation
                        mSwipeRefreshLayout.setRefreshing(false);
                        break;

                    case FINISHED:
                        //Reached end of Data set
                        mSwipeRefreshLayout.setRefreshing(false);
                        break;

                    case ERROR:
                        retry();
                        break;
                }
            }

            @Override
            protected void onError(@NonNull DatabaseError databaseError) {
                super.onError(databaseError);
                mSwipeRefreshLayout.setRefreshing(false);
                databaseError.toException().printStackTrace();
            }
        };

        //Set Adapter to RecyclerView
        mRecyclerView.setAdapter(mAdapter);

Just visit this below URL for reference https://firebaseopensource.com/projects/patilshreyas/firebaserecyclerpagination/app/readme.md/ Here you will find implementation of library which helps to implement Pagination of firebase data in RecyclerView

I hope this will help you!

Shreyas Patil
  • 808
  • 6
  • 17
0

This is how I implemented pagination from firebase database. Consider a case if we have 100 messages. So I first load last 20 messages from firebase i.e. 81 to 100. Then on scroll up I call getMoreMessages() function to load next 20 messages which are 61 to 80 and so on.

public class ChatActivity extends Activity {

        String chat_id = "";
        long mTotalChildren = 0;
        boolean loading = false;
        ChildEventListener childEventListenerMain, childEventListenerPager;
        ChatActivity mContext = ChatActivity.this;
        MessageAdapter messageAdapter;
        @BindView(R.id.pb_messages)
        ProgressBar pb_messages;
        @BindView(R.id.ll_audio)
        LinearLayout llAudio;
        @BindView(R.id.tv_record_time)
        AppCompatTextView tvRecordTime;
        @BindView(R.id.tv_cancel)
        AppCompatTextView tvCancel;
        @BindView(R.id.iv_send)
        AppCompatImageView ivSend;
        @BindView(R.id.iv_record)
        AppCompatImageView ivRecord;
        @BindView(R.id.ab_layout)
        AppBarLayout abLayout;
        @BindView(R.id.messages)
        RecyclerView rvMessages;
        @BindView(R.id.iv_chat_icon)
        SimpleDraweeView ivChatIcon;
        @BindView(R.id.iv_camera)
        AppCompatImageView ivCamera;
        @BindView(R.id.iv_gallery)
        AppCompatImageView ivGallery;
        @BindView(R.id.message_input)
        AppCompatEditText messageInput;
        @BindView(R.id.tv_send)
        AppCompatTextView tvSend;
        @BindView(R.id.toolbar_title)
        AppCompatTextView toolbarTitle;
        @BindView(R.id.toolbar_status)
        AppCompatTextView toolbarStatus;
        @BindView(R.id.ll_send)
        LinearLayout llSend;
        int categoryId, senderId, receiverId, offerId;
        String mLastKey;
        LinearLayoutManager layoutManager;
        private ArrayList<MessageModel> messageArrayList = new ArrayList<>();
        private ArrayList<MessageModel> tempMessageArrayList = new ArrayList<>();

        @Override
        public int getLayoutId() {
            return R.layout.activity_chat;
        }

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            messageAdapter = new MessageAdapter(mContext, messageArrayList) {

            };

            layoutManager = new LinearLayoutManager(mContext);

            rvMessages.setLayoutManager(layoutManager);
            rvMessages.setItemAnimator(new DefaultItemAnimator());
            rvMessages.setAdapter(messageAdapter);
            rvMessages.setHasFixedSize(true);

            rvMessages.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                }

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);

                    if (messageArrayList.size() >= 20 &&
                            !loading && layoutManager.findFirstVisibleItemPosition() == 0 && messageArrayList.size() < mTotalChildren) {

                        loading = true;
                        getMoreMessages();
                    }
                }
            });

            messageAdapter.notifyDataSetChanged();

            //used to scroll up recyclerview when keyboard pops up
            rvMessages.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View view, int left, int top, int right, int bottom,
                                           int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    if (bottom < oldBottom) {
                        rvMessages.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                rvMessages.scrollToPosition(messageArrayList.size() - 1);
                            }
                        }, 100);
                    }
                }
            });

            loading = true;
            getMessages();
        }

        public void getMessages() {

            FirebaseDatabase.getInstance().getReference().child(pathtomsgs).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    Log.e("childrenCount", String.valueOf(+dataSnapshot.getChildrenCount()));
                    mTotalChildren = dataSnapshot.getChildrenCount();
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            });


            Query messageQuery = FirebaseDatabase.getInstance().getReference().child(pathtomsgs).limitToLast(20);

            childEventListenerMain = new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot dataSnapshot, String s) {

                    loading = true;
                    MessageModel messageModel = dataSnapshot.getValue(MessageModel.class);
                    if (messageModel != null) {
                        messageModel.chat_id = chat_id;
                        messageModel.message_id = dataSnapshot.getKey();
                        messageArrayList.add(messageModel);
                        messageAdapter.notifyDataSetChanged();
                        mLastKey = messageArrayList.get(0).message_id;
                        rvMessages.scrollToPosition(messageArrayList.size() - 1);
                    }
                }

                @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) {
                    Toast.makeText(mContext, "Problem loading more images...", Toast.LENGTH_LONG).show();
                }
            };

            messageQuery.addChildEventListener(childEventListenerMain);

            ValueEventListener messageChildSINGLEValueEventListener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    pb_messages.setVisibility(View.GONE);
                    System.out.println("We're done loading messages " + dataSnapshot.getChildrenCount() + " items");
                    loading = false;
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            };

            messageQuery.addListenerForSingleValueEvent(messageChildSINGLEValueEventListener);
        }

        public void getMoreMessages() {
            tempMessageArrayList.clear();

            Query messageQuery = FirebaseDatabase.getInstance().getReference().child(pathtomsgs).orderByKey().endAt(mLastKey).limitToLast(20);

            childEventListenerPager = new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                    loading = true;
                    MessageModel messageModel = dataSnapshot.getValue(MessageModel.class);
                    if (messageModel != null) {

                        messageModel.chat_id = chat_id;
                        messageModel.message_id = dataSnapshot.getKey();
                        tempMessageArrayList.add(messageModel);
                    }
                }

                @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) {
                    Toast.makeText(mContext, "Problem loading more images...", Toast.LENGTH_LONG).show();
                }
            };

            messageQuery.addChildEventListener(childEventListenerPager);

            ValueEventListener messageChildSINGLEValueEventListener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    System.out.println("We're done loading messages " + dataSnapshot.getChildrenCount() + " items");

                    tempMessageArrayList.remove(tempMessageArrayList.size() - 1);

                    messageArrayList.addAll(0, tempMessageArrayList);
                    mLastKey = messageArrayList.get(0).message_id;
                    messageAdapter.notifyDataSetChanged();
                    //rvMessages.scrollToPosition(20);
                    layoutManager.scrollToPositionWithOffset(19, 0);
                    loading = false;
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            };

            messageQuery.addListenerForSingleValueEvent(messageChildSINGLEValueEventListener);
        }


        @Override
        protected void onDestroy() {

            FirebaseDatabase.getInstance().getReference().child(pathtomsgs).removeEventListener(childEventListenerPager);
            FirebaseDatabase.getInstance().getReference().child(pathtomsgs).removeEventListener(childEventListenerMain);

            super.onDestroy();
        }
    }
Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82
0

Android paging library can be used to implement pagination for data fetched from firebase database. Data is displayed in recycler view and paging component fetches pages in response to user scroll events.

Paging data source calls your firebase DAO object passing query parameters for the page to be displayed and results are passed back to paging component using callback provided to it.

Here is a complete example for reference http://www.zoftino.com/firebase-pagination-using-android-paging-library

Arnav Rao
  • 6,692
  • 2
  • 34
  • 31
0

accepted answear dos not work when less then the max. itmes (21) in database. this is my solution build on the accepted answer:

0)set variables

        private String lastMessageKey;
    private int totalItemCount;
    private int lastVisibleItem;
    private boolean isLoadingMoreFollowers = false;
    private boolean hasMoreItems = true;
    private boolean hasShownNoMoreItemsToast = false;
    private boolean initialLoad = true;
    private final int MAX_LOAD_ITEMS = 11;
    private final int VISIBLE_TRESHOLD = 1;

1) set a listener:

       private void setMessageListener() {

        if (childEventListener == null) {

            mmessageArrayList.clear();
            final ArrayList<Mmessage> tempMmessages = new ArrayList<>();
            query = mDatabaseInstace.getReference().child(chat1).child(F.CHATS).child(F.MESSAGES).orderByKey().limitToLast(MAX_LOAD_ITEMS-1);
            childEventListener = new ChildEventListener() {
                @Override
                public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                    Log.d(TAG, "onChildAdded: -----------------> " + dataSnapshot);
                    Log.d(TAG, "onChildAdded: -----------------> " + s);

                    tempMmessages.add(dataSnapshot.getValue(Mmessage.class));
                    preloadMessagePics(tempMmessages);
                    if (initialLoad) {
                        lastMessageKey = tempMmessages.get(0).getMessagePushKey();
                        initialLoad = false;
                    }
                    mmessageArrayList.add(tempMmessages.size()-1, tempMmessages.get(0));
                    messageAdapter.notifyDataSetChanged();
                    tempMmessages.clear();


                }

                @Override
                public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                    Log.d(TAG, "onChildChanged: --------------------------------->");

                }

                @Override
                public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
                    Log.d(TAG, "onChildRemoved: ---------------------------------->");

                }

                @Override
                public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                    Log.d(TAG, "onChildMoved: ------------------------------------>");

                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {
                    Log.d(TAG, "onCancelled: ------------------------------------->");

                }
            };
            query.addChildEventListener(childEventListener);
        }

    }

2) set load more listener:

        private void setLoadMoreListener(){
        setup.RV_messages.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);


                totalItemCount = linearLayoutManager.getItemCount();
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();



                if (!isLoadingMoreFollowers && totalItemCount <= (lastVisibleItem+VISIBLE_TRESHOLD) && (totalItemCount >= MAX_LOAD_ITEMS-1)){

                    if (hasMoreItems) {

                        getMoreMessages();
                        Toast.makeText(homeActivity, "load more items", Toast.LENGTH_SHORT).show();

                    }else {
                        if (!hasShownNoMoreItemsToast) {
                            Toast.makeText(homeActivity, "has no more items", Toast.LENGTH_SHORT).show();
                            hasShownNoMoreItemsToast = true;
                        }
                    }

                }

            }
        });
    }

3) get more items:

       private void getMoreMessages(){

        final ArrayList<Mmessage> tempMmessages = new ArrayList<>();

        isLoadingMoreFollowers = true;
        loadMoreQuery = mDatabaseInstace.getReference().child(chat1).child(F.CHATS).child(F.MESSAGES).orderByKey().endAt(lastMessageKey).limitToLast(MAX_LOAD_ITEMS);
        loadMoreChildEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

                tempMmessages.add(dataSnapshot.getValue(Mmessage.class));
                preloadMessagePics(tempMmessages);

                if (tempMmessages.size() == MAX_LOAD_ITEMS){

                    lastMessageKey = tempMmessages.get(0).getMessagePushKey();
                    Collections.reverse(tempMmessages);
                    tempMmessages.remove(0);
                    mmessageArrayList.addAll(tempMmessages);
                    isLoadingMoreFollowers = false;
                    messageAdapter.notifyDataSetChanged();
                    tempMmessages.clear();
                    return;
                }


                if (lastMessageKey.equalsIgnoreCase(tempMmessages.get(tempMmessages.size()-1).getMessagePushKey())){

                    Collections.reverse(tempMmessages);
                    tempMmessages.remove(0);
                    mmessageArrayList.addAll(tempMmessages);
                    isLoadingMoreFollowers = false;
                    hasMoreItems = false;
                    messageAdapter.notifyDataSetChanged();
                    tempMmessages.clear();

                }

            }

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

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {

            }

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

            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        };
        loadMoreQuery.addChildEventListener(loadMoreChildEventListener);

    }

have fun!!

Ronny Bigler
  • 654
  • 6
  • 12
0

If you want to get list from firebase realtime database descending with pagination you need use smth like this:

Query query = myRef.child("stories").orderByChild("takenAt").endAt(1600957136000L).limitToLast(30);

Full code, two requests

        List<Story> storyList = new ArrayList<>();
        
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        DatabaseReference myRef = database.getReference();
        
        //first request will be look like this
        Query query = myRef.child("stories").orderByChild("takenAt").endAt(1600957136000L).limitToLast(30);
        query.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if (dataSnapshot.exists()) {
                    for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                        Story story = snapshot.getValue(Story.class);
                        storyList.add(story);
                    }
                }
                Collections.reverse(storyList);
            }
            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Log.e("ptg", "onCancelled: ", error.toException());
            }
        });
        
        //second request
        long lastTakenAtInTheStoryList = storyList.get(storyList.size()-1).getTakenAt();
        Query query2 = myRef.child("stories").orderByChild("takenAt").endAt(lastTakenAtInTheStoryList).limitToLast(30);
        query2.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if (dataSnapshot.exists()) {
                    for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                        Story story = snapshot.getValue(Story.class);
                        storyList.add(story);
                    }
                }
                Collections.reverse(storyList);
            }
            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Log.e("ptg", "onCancelled: ", error.toException());
            }
        });
Dyno Cris
  • 1,823
  • 1
  • 11
  • 20
-1

In the past (but no longer) a question regarding implementing Pagination to a query on Firestore was marked as a duplicate of this question I will answer for FireStore here.

Paginate a query on android Firestore

Query query = db.collection("cities")
        .orderBy("population")
        .limit(25);

Query next = query;

 private void loadCities() {

     query = next;
     query.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.
                next = db.collection("cities")
                        .orderBy("population")
                        .startAfter(lastVisible)
                        .limit(25);

            }
        });         
 }

Now just call the loadCities() method whenever you need to load more cities.

robsiemb
  • 6,157
  • 7
  • 32
  • 46
lovish
  • 49
  • 9