35

I have an EndlessRecyclerView at the end of a NestedScrollView. EndlessRecyclerView means: when user scrolls to the bottom of the recyclerView it loads more data. This is already implemented and working elsewhere but when I put the recyclerView inside the NestedScrollView the OnScrollListener events doesn't fire.

XML design:

<NestedScrollView>

     <Other views/>

     <EndlessRecyclerView/>

</NestedScrollView >

Code:

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            // This is never fired! Here is where I implement the logic of EndlessRecyclerView
        }
    });

How do I get scroll event for above case?

I know that is not good to have two scrollable views inside each other. But, how do I have the above case without having two scrollable views?

I already followed this link but it doesn't work: scroll event for recyclerview inside scrollview android

Vasily Kabunov
  • 5,179
  • 12
  • 41
  • 46
Damia Fuentes
  • 4,404
  • 3
  • 26
  • 56

4 Answers4

82

To achieve endless scrolling for recycler view which is under NestedScrollView, you can use "NestedScrollView.OnScrollChangeListener"

nestedScrollView.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
            if(v.getChildAt(v.getChildCount() - 1) != null) {
                if ((scrollY >= (v.getChildAt(v.getChildCount() - 1).getMeasuredHeight() - v.getMeasuredHeight())) &&
                        scrollY > oldScrollY) {
                        //code to fetch more data for endless scrolling
                }
            }
        });

Here v.getChildCount() -1 should give you the recycler view for which you be implementing endless scrolling.

Also scrollY > oldScrollY confirms that the page is being scrolled down.

Reference: NestedScrollView.OnScrollChangeListener

Govind
  • 2,142
  • 22
  • 38
  • Awesome Man I spend whole day seriously to resolve it you make it in two lines. Great this one... But now how can I get the visible items and all so I can fetch new records on server? – Rahul Vats Nov 12 '17 at 10:55
  • @RahulVats In the above code when the if condition is met, it means that user has scrolled down completely and last item is visible. Let's say you have 100 items and initially 20 items are loaded. If the above condition is met, you can download the next set of data (ex: 21-40) from your server. Anyway, you can refer this link for your actual query: https://stackoverflow.com/questions/40726438/android-detect-when-the-last-item-in-a-recyclerview-is-visible?noredirect=1&lq=1 – Govind Nov 13 '17 at 05:21
  • *Call requires API level 23* I think if the View above RecyclerView is not too complicated, we should implement it as different item type of RecyclerView and remove NestedScrollView, it can keep recycling technique also. – Think Twice Code Once Apr 18 '18 at 04:59
  • @ThinkTwiceCodeOnce It doesn't need API level 23. It supports from API 14. You can read more about it here - https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.OnScrollChangeListener.html – Govind Apr 19 '18 at 06:19
  • amazing. Thank you. – Max Zonov May 31 '18 at 18:20
  • 5
    @ThinkTwiceCodeOnce When you set this listener, you should use NestedScrollView.OnScrollChangeListener(), not View.OnScrollChangeListener(). – Smiles Oct 20 '18 at 09:34
  • Thank you very much, perfect :) – ErShakirAnsari Nov 04 '20 at 12:38
2

I had a similar issue, although it was a little different. In my case I had a recycleview within a fragment while the NestedScrollView was in the content_main xml (part of the activity).

I wrapped my recycleview that was within the fragment with SwipeRefreshLayout

This is the code of my fragment:

    <?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/swipe_dummy"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/top_series_recycle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

The only thing is left to do is to disable the SwipeRefreshLayout from the code

mSwipeLayout.isEnabled = false

If you won't do that, when you swipe down it will show endless refresh icon. I wanted to share this solution in case someone will need this functionality or have this issue as well

After you will wrap the recycleview with a SwipeRefreshLayout, you will see addOnScrollListener of the recycleview will be called as usual

Anton Makov
  • 736
  • 1
  • 7
  • 24
2
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener()
        {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
            {
                if (v.getChildAt(v.getChildCount() - 1) != null)
                {
                    if (scrollY > oldScrollY)
                    {
                        if (scrollY >= (v.getChildAt(v.getChildCount() - 1).getMeasuredHeight() - v.getMeasuredHeight()))
                        {
                            //code to fetch more data for endless scrolling
                        }
                    }
                }
            }
        });
ErShakirAnsari
  • 617
  • 6
  • 17
0
nestedScrollView.setOnScrollChangeListener { v: NestedScrollView?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int ->

        val view = nestedScrollView.getChildAt(nestedScrollView.childCount - 1)
        Timber.d("Count==============${nestedScrollView.childCount}")

        val diff = view.bottom - (nestedScrollView.height + nestedScrollView.scrollY)
        Timber.d("diff==============$diff")

        if (diff == 0) {
            //your api call to fetch data
            page++
            apiCall()
        }


    }
VIVEK CHOUDHARY
  • 184
  • 1
  • 5