Search code examples
androidandroid-recyclerviewpaginationinfinite-scrollstaggeredgridlayoutmanager

How to implement endless scroll (pagination) in recyclerview with StaggeredGridLayoutManager


Hello there I have a recyclerview with images loaded from firebase I want to implement endless scroll but the issue I'm getting is with StaggeredGridLayoutManager and I have to use it I cant replace it with grid-layout

Note:- I have seen other answers on this topic but the majority of answers are for LinearLayoutManager and some for GridLayout but there is only one answer I found for StaggeredGridLayoutManager which I don't know why it's not working for me

If you want more references of the code please tell me I will update the question

Profile_Fragment.java

 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_profile, container, false);
    requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    ImageView accountSettings = view.findViewById(R.id.account_Settings);
    RelativeLayout relativeLayout = view.findViewById(R.id.snipet_profile);
    profilePhoto = relativeLayout.findViewById(R.id.circleImageView);
    editProfileButton = relativeLayout.findViewById(R.id.edit_profile_button);
    uploadImageButton = view.findViewById(R.id.upload_image_profile);
    progressBar = view.findViewById(R.id.progressBar_Profile);
    editProfileButton.setOnClickListener(v -> {
        Fragment edit_profile = new Edit_Profile();
        assert getFragmentManager() != null;
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.add(R.id.fragment_container, edit_profile);
        transaction.addToBackStack(String.valueOf(edit_profile));
        transaction.commit();
    });
    uploadImageButton.setOnClickListener(v -> {
        BottomSheet_Upload bottomSheetSettings = new BottomSheet_Upload();
        bottomSheetSettings.show(requireActivity().getSupportFragmentManager(), bottomSheetSettings.getTag());
    });
    accountSettings.setOnClickListener(
            v -> {
                BottomSheet_Settings bottomSheetSettings = new BottomSheet_Settings();
                bottomSheetSettings.show(requireActivity().getSupportFragmentManager(), bottomSheetSettings.getTag());
            }
    );
    postRecyclerView = view.findViewById(R.id.postRecyclerViewProfile);
    StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
    postRecyclerView.setLayoutManager(
            staggeredGridLayoutManager // I have 3 rows
    );


    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");
                }
            }
        }
    });
    postRecyclerView.setVisibility(View.INVISIBLE);
    progressBar.setVisibility(View.VISIBLE);
    initImageLoader();
    setProfileImage();
    mUploads = new ArrayList<>();
    postsAdapter = new PostAdapter(getContext(), mUploads);
    postRecyclerView.setAdapter(postsAdapter);
    postsAdapter.setOnItemClickListener(Profile_Fragment.this);
    return view;
}

private void initImageLoader() {
    UniversalImageLoader universalImageLoader = new UniversalImageLoader(getContext());
    ImageLoader.getInstance().init(universalImageLoader.getConfig());
}

private void setProfileImage() {
    String imgUrl = "www.64.media.tumblr.com/1276b4edef49034af70bda14325385e3/d8872c747cafa206-96/s500x750/aa915fc49a84b5295f0cd44145d655b66eb906a6.jpg";
    UniversalImageLoader.setImage(imgUrl, profilePhoto, null, "https://");
}

private void getData() {
    mStorage = FirebaseStorage.getInstance();
    databaseEventListener = databaseReference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            if (snapshot.exists()) {
                progressBar.setVisibility(View.GONE);
                postRecyclerView.setVisibility(View.VISIBLE);
                mUploads.clear();
                for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                    Upload upload = dataSnapshot.getValue(Upload.class);
                    Objects.requireNonNull(upload).setmKey(dataSnapshot.getKey());
                    mUploads.add(upload);


                }

            }

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

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

Update 1

The main question code is changed with the current answer and Added the adapter class as requested

Problem

After implementing the answer there are no errors but the images are not loading in so I have updated the main code with the added answer

PostAdapter.java

public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
    public static List<Upload> mUploads;
    public Context mcontext;
    private OnItemClickListener mListener;
    /* ShimmerFrameLayout shimmerFrameLayout; */


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

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

    }

    @Override
    public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
        Upload uploadCurrent = mUploads.get(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);
        Glide.with(mcontext)
                .load(uploadCurrent.getmImageUrl())
                .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
                .placeholder(shimmerDrawable)
                .centerCrop()
                .fitCenter()
                .into(holder.imageView);

    }

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

    public void setOnItemClickListener(OnItemClickListener listener) {
        mListener = listener;

    }

    public interface OnItemClickListener {
        void onClick(View view);

        void onItemClick(int position);

        void onDeleteClick(int position);
    }

    public class PostViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,
            View.OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener {
        ShapeableImageView imageView;

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

            itemView.setOnClickListener(this);
            itemView.setOnCreateContextMenuListener(this);
        }

        @Override
        public void onClick(View v) {
            if (mListener != null) {
                int position = getAdapterPosition();
                if (position != RecyclerView.NO_POSITION) {
                    mListener.onItemClick(position);
                }
            }
        }

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            MenuItem delete = menu.add(Menu.NONE, 2, 2, "Delete");
            delete.setOnMenuItemClickListener(this);
        }

        @Override
        public boolean onMenuItemClick(MenuItem item) {
            if (mListener != null) {
                int position = getAdapterPosition();
                if (position != RecyclerView.NO_POSITION) {
                    if (item.getItemId() == 2) {
                        mListener.onDeleteClick(position);
                        return true;
                    }
                }
            }
            return false;
        }
    }
}

Solution

  • For your first problem you already have a solution.

    StaggeredGridLayoutManager staggeredGridLayoutManager = new 
       StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
        postRecyclerView.setLayoutManager(
                staggeredGridLayoutManager // I have 3 rows
        );
        
    

    For second problem:

    postRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener({
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    
            visibleItemCount = staggeredGridLayoutManager .getChildCount();
            totalItemCount = staggeredGridLayoutManager .getItemCount();
            int[] firstVisibleItems = null;
            firstVisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
            if(firstVisibleItems != null && firstVisibleItems.length > 0) {
                pastVisibleItems = firstVisibleItems[0];
            }
    
            if (loading) {
                if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
                    loading = false;
                    getData()
                }
            }
        }
     });
     ..........
     ..........
      private void getData() {
        mStorage = FirebaseStorage.getInstance();
        databaseEventListener = databaseReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if (snapshot.exists()) {
                    progressBar.setVisibility(View.GONE);
                    postRecyclerView.setVisibility(View.VISIBLE);
                    mUploads.clear();
                    for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                        Upload upload = dataSnapshot.getValue(Upload.class);
                        Objects.requireNonNull(upload).setmKey(dataSnapshot.getKey());
                        mUploads.add(upload);
                    }
                }
                //notify the adapter
                postsAdapter.notifyDataSetChanged();
                loading = true;
            }
    
            @Override
            public void onCancelled(@NonNull DatabaseError error) {
               loading = true;
            }
        });
    }
    

    You might have to call the getData() initially in your onCreate() so that some data loads up on the screen and you have a scroll behavior.

    Update: The second parameter in a StaggeredGridLayoutManager is orientation so instead of context you have to pass orientation StaggeredGridLayoutManager.VERTICAL.