How to send pagination requests to firebase for endlessly scrollable recyclerview

Viewed 269

I'm trying to include pagination in my app what I want that for eg in my firebase I have 500 images that are to be shown in the recycler view what I want is first 10 or 15 images are loaded in the recycler view at the beginning and when the user reaches to the end of the last item(image) it loads next 10 images (just like Instagram or facebook )

Note:- if anyone wants more reference of my code please tell me I will update my question

Here is my code

Home_Fragment.java

private boolean loading = true;
    private int pastVisibleItems, visibleItemCount, totalItemCount;

    @SuppressLint("SourceLockedOrientationActivity")
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_home, container, false);
        requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        MaterialToolbar materialToolbar = view.findViewById(R.id.toolbar);
        materialToolbar.setOnMenuItemClickListener(toolbarItemClickListener);
        postRecyclerView = view.findViewById(R.id.recyclerViewHome);
        shimmerFrameLayout = view.findViewById(R.id.shimmerEffect);
//        this is for one item per scroll
//        SnapHelper snapHelper = new PagerSnapHelper();
//        snapHelper.attachToRecyclerView(verticalRecyclerView);
        postRecyclerView.setAdapter(postsAdapter);
//        listState = savedInstanceState.getParcelable("ListState");

        StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL);
        postRecyclerView.setLayoutManager(
                staggeredGridLayoutManager
        );
        getData();


        postRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {

                visibleItemCount = staggeredGridLayoutManager.getChildCount();
                totalItemCount = staggeredGridLayoutManager.getItemCount();
                int[] firstVisibleItems = null;
                firstVisibleItems = staggeredGridLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
                if (firstVisibleItems != null && firstVisibleItems.length > 0) {
                    pastVisibleItems = firstVisibleItems[0];
                }

                if (loading) {
                    if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
                        loading = false;
                        getData();
                        Log.d("tag", "LOAD NEXT ITEM");
                    }
                }
            }
        });
//        setupFirebaseAuth();
        shimmerFrameLayout.startShimmer();
        mUploads = new ArrayList<>();
        postsAdapter = new PostAdapter_Home(getContext(), mUploads);
        postRecyclerView.setAdapter(postsAdapter);
        postRecyclerView.scrollToPosition(Home_Fragment.saved_position);
        return view;
    }


    private void getData() {
        databaseReference.addValueEventListener(new ValueEventListener() {
            @SuppressLint("NotifyDataSetChanged")
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if (snapshot.exists()) {
                    shimmerFrameLayout.stopShimmer();
                    shimmerFrameLayout.setVisibility(View.GONE);
                    postRecyclerView.setVisibility(View.VISIBLE);
                    mUploads.clear();
                    for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                        Upload upload = dataSnapshot.getValue(Upload.class);
                        assert upload != null;
                        upload.setmKey(dataSnapshot.getKey());
                        mUploads.add(upload);


                    }

                }
                postsAdapter.setUploads(mUploads);

                //notify the adapter
                postsAdapter.notifyDataSetChanged();
                loading = true;
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                loading = true;
            }
        });
    }

PostAdapter_Home.java // Adapter Class

public class PostAdapter_Home extends RecyclerView.Adapter<PostAdapter_Home.PostViewHolder> {
    public static List<Upload> mUploads;
    public Context mcontext;

    public PostAdapter_Home(Context context, List<Upload> uploads) {
        mUploads = uploads;
        mcontext = context;
    }


    @NonNull
    @Override
    public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        view = LayoutInflater.from(mcontext).inflate(R.layout.ex_home, parent, false);
        return new PostViewHolder(view);

    }

    @Override
    public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
        Shimmer shimmer = new Shimmer.ColorHighlightBuilder()
                .setBaseColor(Color.parseColor("#F3F3F3"))
                .setBaseAlpha(1)
                .setHighlightColor(Color.parseColor("#E7E7E7"))
                .setHighlightAlpha(1)
                .setDropoff(50)
                .build();
        ShimmerDrawable shimmerDrawable = new ShimmerDrawable();
        shimmerDrawable.setShimmer(shimmer);
        Upload uploadCurrent = mUploads.get(position);
        Glide.with(mcontext)
                .load(uploadCurrent.getmImageUrl())
                .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
                .placeholder(shimmerDrawable)
                .centerCrop()
                .fitCenter()
                .into(holder.imageView);

//        holder.imageView.setOnClickListener(view -> changeScaleType(holder, position));

    }


    @Override
    public int getItemCount() {
        return mUploads.size();
    }

    public void setUploads(List<Upload> uploads){
        mUploads=uploads;
    }
    public static class PostViewHolder extends RecyclerView.ViewHolder {

        private final ShapeableImageView imageView;

        public PostViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.imagePostHome);

        }


    }
}

Update // Added Upload.java file code as requested in the comment section

Upload.java

package com.example.myappnotfinal.AdaptersAndMore;

import com.google.firebase.database.Exclude;

public class Upload {
    private String mImageUrl;
    private String mKey;
    private String mUserName;
    private String mComment;

    public Upload() {

    }

    public Upload(String imageUrl) {
        mImageUrl = imageUrl;
    }

    public String getmUserName() {
        return mUserName;
    }

    public void setmUserName(String mUserName) {
        this.mUserName = mUserName;
    }

    public String getmComment() {
        return mComment;
    }

    public void setmComment(String mComment) {
        this.mComment = mComment;
    }

    public String getmImageUrl() {
        return mImageUrl;
    }

    public void setmImageUrl(String mImageUrl) {
        this.mImageUrl = mImageUrl;
    }

    @Exclude
    public String getmKey() {
        return mKey;
    }

    @Exclude
    public void setmKey(String Key) {
        this.mKey = Key;
    }
}
2 Answers

First you should separate fetching data logic from UI. Then your scroll controller (RecyclerView) should control which images should be called to download (so when it's loaded it should ask for first batch of images, when scroll happens - ask for another batch, etc.). And downloading and caching images is not a trivial task, that's why we have Glide and Coil in Android world.

As a side note please take a look at https://developer.android.com/topic/libraries/architecture/paging/v3-paged-data and https://developer.android.com/topic/libraries/architecture/paging.

Please check what you're adding to the list of mUploads because every time you add items, you clear the list upfront mUploads.clear(); which may be not what you wanted.

Some notes:

  • dont make it static public static List<Upload> mUploads;
  • don't need to store mcontext, Context is already there
  • strip the problem out of unncessary stuff (e.g. drop shimmer, fix the problem, add shimmer when fixed)

Here what you need to do is setLimit for pagination process.

Here is and example:

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

So here you need to send last node id each time to get new list. After getting response just add your data to ArrayList or what ever logic you are using. Then just update RecyclerView like this:

 blogRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                isScrolling = true;
            }
        }

        @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            currentItems = manager.getChildCount();
            totalItems = manager.getItemCount();
            scrollOutItems = manager.findFirstVisibleItemPosition();

            if (isScrolling && (currentItems + scrollOutItems == totalItems) && nextPage != null) {
                isScrolling = false;
                progressBar.setVisibility(View.VISIBLE);
                 getUsers(mlastNodeId);//here your last id will be
            }
        }
    });
}
Related