Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Endless Scrolling RecyclerView is not working when items in list are minimum then screen size

I have successfully implemented the Endless Scrolling using Recycler view. I am using this code and this works perfectly but except with the following case

Case 1: When the list has 2 items which are lesser then the screen size so its looks like the OnLoadMore calls again as it finds itself arriving to the end of the list

What I am doing: I am getting the list from 1 fragment and then send this list to the other fragment and in that fragment there is a RecyclerView with the onScroll listner here it is as follows :

recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager) {
        @Override
        public void onLoadMore(int page, int totalItemsCount) {
            // do something...

                currentVisiblePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
                pageNumber = pageNumber + 1;
                CallingMyApi();

        }
    });

Confusions:

  • My RecyclerView add on scroll listener run first time when the fragments gets initialized. where as there is no scrolling performed.
  • if there are lesser items let say 2 it runs always on fragment initiallized , in case of many items OnLoadMore methos does not runs.

So i can guess that it is problem due to the less items in the list view How to control that please help me ??

Update 1: My Fragment Code

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    mAdapter = new EnrollmentSearchAdapter(getActivity(), parentList);

    rvSearchEnrollments.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager) {
        @Override
        public void onLoadMore(int page, int totalItemsCount) {
            // do something...

            if(parentList!=null && parentList.size()>3) { // just checking my own that 3 items should not be minimum to screen size , where as here it should be more flexible solution 
                currentVisiblePosition = ((LinearLayoutManager) rvSearchEnrollments.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
                pageNumber = pageNumber + 1;
                SearchSundaySchoolParentsCouples searchSundaySchoolParentsCouples = new SearchSundaySchoolParentsCouples(keyword, 20, pageNumber, getActivity());
                searchSundaySchoolParentsCouples.execute();
            }
        }
    });

    rvSearchEnrollments.addItemDecoration(new SimpleDividerItemDecoration(getActivity()));

    rvSearchEnrollments.setAdapter(mAdapter);


}
like image 720
A.s.ALI Avatar asked Aug 22 '16 10:08

A.s.ALI


People also ask

How scroll RecyclerView to bottom in android programmatically?

mLinearLayoutManager = new LinearLayoutManager(this); recyclerView. setLayoutManager(mLinearLayoutManager); 3). On your Button onClick , do this to scroll to the bottom of your RecyclerView .

Is RecyclerView scrollable?

To help you build apps with lists, Android provides the RecyclerView . RecyclerView is designed to be very efficient, even with large lists, by reusing, or recycling, the views that have scrolled off the screen.


2 Answers

Use the EndlessRecyclerViewScrollListener OnScrollListener subclass (it's from a popular Gist). You'll need to change a few things in order to get an endless Scrolling RecyclerView working when the items in the list are minimum then screen size.

Here's what you need to add to the EndlessRecyclerViewScrollListener class:

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
{
    super.onScrollStateChanged(recyclerView, newState);
    // Don't do any work until after the ScrollEvent has ended, so do the following check.
    if (newState == RecyclerView.SCROLL_STATE_IDLE)
    {
        // supply a positive number to recyclerView.canScrollVertically(int direction) to check if scrolling down.
        boolean canScrollDownMore = recyclerView.canScrollVertically(1);
        // If recyclerView.canScrollVertically(1) returns false it means you're at the end of the list.
        if (!canScrollDownMore)
        {
            //call the overridden onScrolled() method in our EndlessRecyclerViewScrollListener class.
            // supply any positive number to the third argument to indicate that we've scrolled downward.
            onScrolled(recyclerView, 0, 1);
        }
    }
}

Explanation: We need to implement the onScrollStateChanged() method because onScrolled() doesn't get called when at the end of a list, or at least it doesn't get called when the items in the list are minimum then screen size.

Here's the entire EndlessRecyclerViewScrollListener class with the updates that you'll need:

public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener
{
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;

private RecyclerView.LayoutManager layoutManager;

public void setLayoutManager(RecyclerView.LayoutManager layoutManager)
{
    this.layoutManager = layoutManager;
}

public RecyclerView.LayoutManager getLayoutManager()
{
    return layoutManager;
}

public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager)
{
    this.layoutManager = layoutManager;
}

public EndlessRecyclerViewScrollListener(GridLayoutManager layoutManager, int visibleThreshold)
{
    this.layoutManager = layoutManager;
    this.visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}

public EndlessRecyclerViewScrollListener(
        StaggeredGridLayoutManager layoutManager,
        int visibleThreshold)
{
    this.layoutManager = layoutManager;
    this.visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}

public int getLastVisibleItem(int[] lastVisibleItemPositions)
{
    int maxSize = 0;
    for (int i = 0; i < lastVisibleItemPositions.length; i++)
    {
        if (i == 0)
        {
            maxSize = lastVisibleItemPositions[i];
        }
        else if (lastVisibleItemPositions[i] > maxSize)
        {
            maxSize = lastVisibleItemPositions[i];
        }
    }
    return maxSize;
}

// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
@Override
public void onScrolled(RecyclerView view, int dx, int dy)
{
    if (dy < 1)
    {
        // don't do anything when scrolling up.
        return;
    }
    int lastVisibleItemPosition = 0;
    int totalItemCount = layoutManager.getItemCount();

    if (layoutManager instanceof StaggeredGridLayoutManager)
    {
        int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(null);
        // get maximum element within the list
        lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
    }
    else if (layoutManager instanceof GridLayoutManager)
    {
        lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
    }
    else if (layoutManager instanceof LinearLayoutManager)
    {
        lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
    }

    // If the total item count is zero and the previous isn't, assume the
    // list is invalidated and should be reset back to initial state
    if (totalItemCount < previousTotalItemCount)
    {
        this.currentPage = this.startingPageIndex;
        this.previousTotalItemCount = totalItemCount;
        if (totalItemCount == 0)
        {
            this.loading = true;
        }
    }
    // If it’s still loading, we check to see if the dataset count has
    // changed, if so we conclude it has finished loading and update the current page
    // number and total item count.
    if (loading && (totalItemCount >= previousTotalItemCount))
    {
        loading = false;
        previousTotalItemCount = totalItemCount;
    }

    // If it isn’t currently loading, we check to see if we have breached
    // the visibleThreshold and need to reload more data.
    // If we do need to reload some more data, we execute onLoadMore to fetch the data.
    // threshold should reflect how many total columns there are too
    if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount)
    {
        currentPage++;
        onLoadMore(currentPage, totalItemCount, view);
        loading = true;
    }
}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
{
    super.onScrollStateChanged(recyclerView, newState);
    if (newState == RecyclerView.SCROLL_STATE_IDLE)
    {
        // supply a positive number to recyclerView.canScrollVertically(int direction) to check if scrolling down.
       boolean canScrollDownMore = recyclerView.canScrollVertically(1);
       // If recyclerView.canScrollVertically(1) returns false it means you're at the end of the list.
       if (!canScrollDownMore)
       {
           onScrolled(recyclerView, 0, 1);
       }
    }
}

// Call this method whenever performing new searches on in onRefresh() if using SwipeRefresh
public void resetState()
{
    this.currentPage = this.startingPageIndex;
    this.previousTotalItemCount = 0;
    this.loading = true;
}

// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view);

}

Brief sample of usage: (more info here)

    public class MainActivity extends Activity {
    // Store a member variable for the listener
    private EndlessRecyclerViewScrollListener scrollListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       // Configure the RecyclerView
       RecyclerView rvItems = (RecyclerView) findViewById(R.id.rvContacts);
       LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
       rvItems.setLayoutManager(linearLayoutManager);
       // Retain an instance so that you can call `resetState()` for fresh searches
       scrollListener = new EndlessRecyclerViewScrollListener(linearLayoutManager) {
           @Override
           public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
               // Triggered only when new data needs to be appended to the list
               // Add whatever code is needed to append new items to the bottom of the list
               loadNextDataFromApi(page);
           }
      };
      // Adds the scroll listener to RecyclerView
      rvItems.addOnScrollListener(scrollListener);
  }

  // Append the next page of data into the adapter
  // This method probably sends out a network request and appends new data items to your adapter. 
  public void loadNextDataFromApi(int offset) {
      // Send an API request to retrieve appropriate paginated data 
      //  --> Send the request including an offset value (i.e `page`) as a query parameter.
      //  --> Deserialize and construct new model objects from the API response
      //  --> Append the new data objects to the existing set of items inside the array of items
      //  --> Notify the adapter of the new items made with `notifyItemRangeInserted()`
  }
}

You may need to modify things a bit to get it working in your scenario, but the code provided is a GREAT starting point.

like image 163
Sakiboy Avatar answered Oct 24 '22 20:10

Sakiboy


You can do your load more task something like this in recyclerview if list are minimum then screen size

@Override
 public void onBindViewHolder(DataObjectHolder holder, int position) {
            holder.label.setText(mDataset.get(position).getmText1());
            holder.dateTime.setText(mDataset.get(position).getmText2());

           if (position == this.getItemCount() - 1 &&  !loading){
                // do your load more task here
           }
  }
like image 30
Anand Kumar Jha Avatar answered Oct 24 '22 20:10

Anand Kumar Jha