20

I already tried to implement the endless scrolling for LinearLayoutManager and it is successful and tried to copy the LinearLayoutManager implementation to StaggeredGridLayoutManager but it doesn't work.

I just want to get the firstVisibleItem.

in LinearLayoutManager :

int firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition(int);

but in StaggeredGridLayoutManager is :

int firstVisibleItem = staggeredGridLayoutManager.findFirstVisibleItemPositions(int[])

How to get the firstVisibleItem using (int) not (int[])?

Is there any good approach/implementation about this?

Thanks in advance.

Konrad Krakowiak
  • 11,733
  • 10
  • 53
  • 44
Fran Ceriu
  • 221
  • 2
  • 5

4 Answers4

26

I got it working:

You can use one of two methods in the StaggeredGridLayoutManager:

  1. findFirstVisibleItemPositions(int[])
  2. findFirstCompletelyVisibleItemPositions(int[])

Pass an empty int array that will get initialized with the positions and use the one that makes sense for you.

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

mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener({
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

        visibleItemCount = mLayoutManager.getChildCount();
        totalItemCount = mLayoutManager.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;
                Log.d("tag", "LOAD NEXT ITEM");
            }
        }
    }
});
Lamorak
  • 10,190
  • 8
  • 44
  • 55
Matthew
  • 3,231
  • 2
  • 23
  • 25
  • really appreciated for this solution. It works great as I want. Actually in updated AppCompactLibrary, they have some method to do it. But for some reason, I am not able to update the library. So I was getting stuck. Your answer give me proper solution to that. Thanks a lot. – Shreyash Mahajan Mar 01 '16 at 11:32
7

This is my implementation of a ScrollListener.

Here's my code for implementing the ScrollListener:

private EndlessScrollListener scrollListener =
    new EndlessScrollListener(new EndlessScrollListener.RefreshList() {
        @Override public void onRefresh(int pageNumber) {
            //Here you can execute server connection or anything else to update data and present with Recycler view
            // Notice: It is sync method

        }
    });

recyclerView.addOnScrollListener(scrollListener);

Custom ScrollListener class:

class EndlessScrollListener extends RecyclerView.OnScrollListener {
    private boolean isLoading;
    private boolean hasMorePages;
    private int pageNumber = 0;
    private RefreshList refreshList;
    private boolean isRefreshing;
    private int pastVisibleItems;



    public EndlessScrollListener(RefreshList refreshList) {
        this.isLoading = false;
        this.hasMorePages = true;
        this.refreshList = refreshList;
    }

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

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

        if ((visibleItemCount + pastVisibleItems) >= totalItemCount && !isLoading) {
            isLoading = true;
            if (hasMorePages && !isRefreshing) {
                isRefreshing = true;
                new Handler().postDelayed(new Runnable() {
                    @Override public void run() {
                        refreshList.onRefresh(pageNumber);
                    }
                }, 200);
            }
        } else {
            isLoading = false;
        }
    }

    public void noMorePages() {
        this.hasMorePages = false;
    }

    public void notifyMorePages() {
        isRefreshing = false;
        pageNumber = pageNumber + 1;
    }



    public interface RefreshList {
        void onRefresh(int pageNumber);
    }
}
Edric
  • 18,215
  • 11
  • 68
  • 81
Aleksey Timoshchenko
  • 3,364
  • 1
  • 33
  • 61
  • 1
    is refreshing is always true, this method must be public and be called in "onRefresh" – Pablo Cegarra Feb 23 '18 at 07:32
  • @PabloCegarra Could you please explain one more time? I don't understand what do you mean? The main idea of the listener is let to developer know when the user touches the end of the list. So this event he gets in 'onRefresh()'. Or am I wrong? – Aleksey Timoshchenko Feb 23 '18 at 16:25
  • 1
    Yes its working like a charm for me, but the example is confusing cause notifyMorePages is not public – Pablo Cegarra Feb 23 '18 at 16:29
  • It is working but EndlessScrollListener constructor and RefreshList are not public that why I faced the problem, I was using it in Kotlin, and it is working only one time when it is initialized when I scroll it does not work, Any Idea, what I'm doing wrong. Where I notify more pages. – Farhana Naaz Ansari Apr 28 '18 at 10:35
  • @farhana I have used this code to load next part of data(it was a lot of time ago) , now there is one more good solution that I am using https://developer.android.com/topic/libraries/architecture/paging – Aleksey Timoshchenko Apr 29 '18 at 10:36
  • @AlekseyTimoshchenko yes Architecture Component is excellent, but my project is not set to that standard. – Farhana Naaz Ansari Apr 29 '18 at 15:25
  • Hello sir ,What is Refresh list ?? – Ravish Sharma Jun 21 '18 at 13:05
0

To implement EndlessRecyclerOnScrollListener first create EndlessRecyclerOnScrollListener java class

   import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;

public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
    public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
    private int scrolledDistance = 0;
    private boolean controlsVisible = false;

    private boolean loading = true; // True if we are still waiting for the last set of data to load.
    private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.

    private int pastVisibleItems, visibleItemCount, totalItemCount;

    private int current_page = 1;

    private StaggeredGridLayoutManager mStaggeredGridLayoutManager;

    public EndlessRecyclerOnScrollListener(StaggeredGridLayoutManager staggeredGridLayoutManager) {
        this.mStaggeredGridLayoutManager = staggeredGridLayoutManager;

    }

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

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


        if (loading) {
            if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount)
                <= (pastVisibleItems + visibleThreshold)) {
            // End has been reached

            // Do something
            current_page++;

            onLoadMore(current_page);

            loading = true;
        }

        if (scrolledDistance > 1 && controlsVisible) {
            controlsVisible = false;
            scrolledDistance = 0;
        } else if (scrolledDistance < -1 && !controlsVisible) {
            controlsVisible = true;
            scrolledDistance = 0;
        }

        if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
            scrolledDistance += dy;
        }
    }

    public abstract void onLoadMore(int current_page);

    ;
}

After into your activity or fragment (this example is for fragment) use the next code

    RecyclerView mRecyclerView;
StaggeredGridLayoutManager mStaggeredGridLayoutManager;

private RecyclerView.Adapter adapter;

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle saveInstanceState) {

        View v = inflater.inflate(R.layout.notices_layout, container, false);

        mRecyclerView = (RecyclerView) v.findViewById(R.id.listaNoticias);
 mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(
                2, //number of grid columns
                GridLayoutManager.VERTICAL);

//Sets the gap handling strategy for StaggeredGridLayoutManager
        mStaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);

        mRecyclerView.setLayoutManager(mStaggeredGridLayoutManager);

//initializing our adapter
adapter = new MyAdapter(list, getContext());

//Adding adapter to recyclerview
mRecyclerView.setAdapter(adapter);

mRecyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(mStaggeredGridLayoutManager) {
            @Override
            public void onLoadMore(int current_page) {
                // do something...
                getData(current_page);
            }
        });

return v;
}
Vladimir Salguero
  • 4,156
  • 2
  • 30
  • 39
-1
int mSpanCount = 2;

int[] into = new int[mSpanCount];

int firstVisibleItem = staggeredGridLayoutManager.findFirstVisibleItemPositions(into)[0];
Pratik Butani
  • 51,868
  • 51
  • 228
  • 375