28

How to implement pagination of recyclerview that is within NestedScrollView?

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
Jatin
  • 1,650
  • 3
  • 17
  • 32
  • I tried to implement addOnScrollListener of recyclerview but it calls everytime when I bind my data to recyclerview. It should call on page end only – Jatin Oct 09 '17 at 04:40

4 Answers4

56

Follow this steps :

1. Set nested scrolling enabled false of recycler view.

recyclerView.setNestedScrollingEnabled(false);

2. Add scroll listner to nested scrollview.

 mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
           @Override
           public void onScrollChanged()
           {
                    View view = (View)mScrollView.getChildAt(mScrollView.getChildCount() - 1);

                    int diff = (view.getBottom() - (mScrollView.getHeight() + mScrollView
                                    .getScrollY()));

                    if (diff == 0) {
                       // your pagination code
                    }
           }
  });
AskNilesh
  • 67,701
  • 16
  • 123
  • 163
Vidhi Dave
  • 5,614
  • 2
  • 33
  • 55
  • 10
    its calling all pagination at once – Eldhopj Mar 19 '19 at 10:52
  • @Eldhopj May be there is issue in your pagination code. as this works perfect for me. – Vidhi Dave Mar 20 '19 at 04:09
  • But When I am using recyclerview inside nestedscrollview, Recyclerview becoming heavy - Skipped frames.After so many scrolls my RecyclerView starting to lag.getting error : Skipped 186 frames! The application may be doing too much work on its main thread. How can I fix this? – user2162130 Feb 05 '21 at 09:23
  • Hi @user2162130 have disabled scrolling of recyclerview ? this can happen when there is both scrolling views. you need to disable scrolling of recyclerview – Vidhi Dave Feb 09 '21 at 04:50
  • @VishvaDave Yes I have already disabled scrolling of recyclerview then after that recycler view becoming heavy and lagging. Sometime app is also crashed due to this. – user2162130 Feb 09 '21 at 09:36
  • @user2162130 It's strange issue. as this is the only solution while we use recyclerview inside scrollview. it can be the case that your recyclerview is having some operations i.e timers or loop for rendering data. then it can become heavy if not managed properly then, other then that can try same nested scrolling enabled false from both java file and xml. – Vidhi Dave Feb 09 '21 at 10:09
  • @VishvaDave if I remove nestedscrollview then my recyclerview works properly no any lagging, working smoothly but if I use same recyclerview inside nestedscrollview then it's lagging and heavy and after some time it's crash. I have also set nested scroll enabled false from both java and xml file. – user2162130 Feb 09 '21 at 11:14
  • The issue here is that if the RecyclerView is placed inside a NestedScrollView, in that case the views are not recycled. This means that there is no performance benefits which we get from recycler view. – Tejas Sherdiwala Jun 02 '21 at 06:38
9

if you are using Kotlin your code will be looks like

 scroll?.viewTreeObserver?.addOnScrollChangedListener {
        val view = scroll.getChildAt(scroll.childCount - 1)
        Timber.d("Count==============${scroll.childCount}")

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

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

and last but the not the least set RecyclerView scrolling false

 ViewCompat.setNestedScrollingEnabled(recyclerView, false)
shahid17june
  • 1,441
  • 1
  • 10
  • 15
3

I could get the solution setting OnScrollChangeListener in the nestedScrollView.

The field isLoading should be changed everytime you load the items, for example if you are using retrofit. You could set it as true before It start running and as false when you get the response or the failure.

The field isLastPage should be changed everytime you get items and check if this page was the last one.

I'm using kotlin.

private var isLoading = false

private var isLastPage = false

nestedScrollView.setOnScrollChangeListener { v: NestedScrollView?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int ->

            val nestedScrollView = checkNotNull(v){
                return@setOnScrollChangeListener
            }

            val lastChild = nestedScrollView.getChildAt(nestedScrollView.childCount - 1)

            if (lastChild != null) {

                if ((scrollY >= (lastChild.measuredHeight - nestedScrollView.measuredHeight)) && scrollY > oldScrollY && !isLoading && !isLastPage) {

                    //get more items
                }
            }
        }

And of course you need to set the field isNestedScrollingEnabled as false

myRecyclerView.isNestedScrollingEnabled = false
jmarkstar
  • 1,335
  • 14
  • 21
0

I had the same problem and based on Android documentation:

Never add a RecyclerView or ListView to a scroll view. Doing so results in poor user interface performance and a poor user experience.

I solved my problem using bellow code:

<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/appbar_layout"
    android:background="@color/white"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.google.android.material.appbar.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"
        app:titleEnabled="false">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <!-- Put here elements that you need above the recycler view -->

        </LinearLayout>

    </com.google.android.material.appbar.CollapsingToolbarLayout>

</com.google.android.material.appbar.AppBarLayout>
<!-- RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:nestedScrollingEnabled="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:scrollbars="vertical"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>
Alireza Barakati
  • 1,016
  • 2
  • 9
  • 23
  • what if there is ta tablayout below appbarlayout? and tablayout may have two tabs with different fragments which have recyclerview in it? then this code wont work. so its useless solution – Kishan Solanki Mar 04 '22 at 03:41
  • it works awfully and you can't wrap all of that inside swipe to refresh view – user924 Nov 29 '22 at 18:34