91

Currently I am using the follow code to check whether SwipeRefreshLayout should be enabled.

private void laySwipeToggle() {
    if (mRecyclerView.getChildCount() == 0 || mRecyclerView.getChildAt(0).getTop() == 0) {
        mLaySwipe.setEnabled(true);
    } else {
        mLaySwipe.setEnabled(false);
    }
}

But here is the problem. When it's scrolled to another item's view's boundary mRecyclerView.getChildAt(0).getTop() also returns 0.

The problem

Is there something like RecyclerView.isScrolledToBottom() or RecyclerView.isScrolledToTop()?

EDIT: (mRecyclerView.getChildAt(0).getTop() == 0 && linearLayoutManager.findFirstVisibleItemPosition() == 0) kind of does the RecyclerView.isScrolledToTop(), but what about RecyclerView.isScrolledToBottom()?

Saren Arterius
  • 1,330
  • 1
  • 11
  • 15
  • I suppose the latter can be achieved by verifying the bottom of the recyclerview against the last child i.e something on the lines of mRecyclerView.getBottom()== linearLayoutmanager.findViewbyPosition(adapter.getItemCount() - 1).getBottom() – humblerookie Aug 24 '15 at 13:27
  • @Saren Arterius you might want to take a look at [this](http://stackoverflow.com/a/33515549/1118886) – Sheraz Ahmad Khilji Nov 04 '15 at 07:05

12 Answers12

119

The solution is in the layout manager.

LinearLayoutManager layoutManager = new LinearLayoutManager(this);

// Add this to your Recycler view
recyclerView.setLayoutManager(layoutManager);

// To check if at the top of recycler view
if(layoutManager.firstCompletelyVisibleItemPosition()==0){
    // Its at top
}

// To check if at the bottom of recycler view
if(layoutManager.lastCompletelyVisibleItemPosition()==data.size()-1){
    // Its at bottom
}

EDIT

In case your item size is larger than the screen use the following to detect the top event.

RecyclerView recyclerView = (RecyclerView) view;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

int pos = linearLayoutManager.findFirstVisibleItemPosition();

if(linearLayoutManager.findViewByPosition(pos).getTop()==0 && pos==0){
    return true;
}

PS: Actually, if you place the RecyclerView directly inside the SwipeRefreshview you wouldn't need to do this

olfek
  • 3,210
  • 4
  • 33
  • 49
humblerookie
  • 4,717
  • 4
  • 25
  • 40
  • No, it still happens when the RecyclerView is the direct child of SwipeRefreshLayout. But checking the firstCompletelyVisibleItemPosition fixes the problem. I need to point something here. If none of the items are fully visible firstCompletelyVisibleItemPosition() returns -1. So this solution will not work if the first item doesn't fit into the RecyclerView when it is scrolled to top. But it is a very rare situation. – eluleci Mar 31 '15 at 10:43
  • 1
    @eluleci : Check for whether list is on top RecyclerView rc=(RecyclerView)view; LinearLayoutManager lm= (LinearLayoutManager) rc.getLayoutManager(); if(lm.findViewByPosition(lm.findFirstVisibleItemPosition()).getTop()==0 && lm.findFirstVisibleItemPosition()==0) return true; //This would work evn if the item is of a larger size than screen – humblerookie Apr 27 '15 at 10:03
  • Great! My rows are short enough but I think it'll will be helpful to others. Thanks – eluleci Apr 28 '15 at 08:52
  • 2
    This would fail if the item (first or last) is larger than the entire screen. Because, `first/lastCompletelyVisibleItemPosition()` will return -1, implies, there is no fully visible item at all. – C-- Aug 20 '15 at 10:49
  • @SubinSebastian : I so wish people would take time to read the complete answer. Th edit on Jun 1 mentions the very case – humblerookie Oct 04 '15 at 14:19
  • Only `lastVisibleItemPosition` worked with me without `Completely` – Hamzeh Soboh Jan 07 '16 at 15:38
  • 4
    Both `lastVisibleItemPosition` and `lastCompletelyVisibleItemPosition` don't work. (cannot resolve method). Help? – Stardust Jul 07 '16 at 22:49
  • Hello, I solved my problem - use `final android.support.v7.widget.LinearLayoutManager layoutManager = new LinearLayoutManager(this);` (see http://stackoverflow.com/questions/29327013/android-cannot-resolve-method-findfirstvisibleitemposition) – Stardust Jul 07 '16 at 23:02
  • Also, use `findLastCompletelyVisibleItemPosition`. – Stardust Jul 07 '16 at 23:03
  • 4
    `findLastCompletelyVisibleItemPosition` will FAIL if the last item's height is greater than the screen height. It means that the last item is visible, but not completely visible, so `layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION)`. Use `lastVisibleItemPosition` and check for the bottom of the last view in layout will be safer. – gone Aug 29 '16 at 05:50
  • recyclerView.getLayoutManager() doesn't have those methods, because it returns a LayoutManager and not a LinearLayoutManager(which has the method you want) – Dragos Rachieru Mar 13 '18 at 16:24
  • @DragosRachieru What am I missing here, Isn't that what's explained in the solution via code? – humblerookie Mar 14 '18 at 03:01
  • i was explaining to @Stardust why there's no such method – Dragos Rachieru Mar 16 '18 at 09:11
90

You can try recyclerView.canScrollVertically(int direction), if you just need to know whether it possible to scroll or not.

Direction integers:

  • -1 for up
  • 1 for down
  • 0 will always return false.
Manoj Perumarath
  • 9,337
  • 8
  • 56
  • 77
Penzzz
  • 2,814
  • 17
  • 23
33

In order to check whether RecyclerView is scrolled to bottom. Use the following code.

/**
     * Check whether the last item in RecyclerView is being displayed or not
     *
     * @param recyclerView which you would like to check
     * @return true if last position was Visible and false Otherwise
     */
    private boolean isLastItemDisplaying(RecyclerView recyclerView) {
        if (recyclerView.getAdapter().getItemCount() != 0) {
            int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
            if (lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1)
                return true;
        }
        return false;
    }

Some Additional info

If you want to implement ScrollToBottom in RecyclerView when Edittext is tapped then i recommend you add 1 second delay like this:

 edittext.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP)
                    if (isLastItemDisplaying(recyclerView)) {
// The scrolling can happen instantly before keyboard even opens up so to handle that we add 1 second delay to scrolling
                        recyclerView.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount() - 1);

                            }
                        }, 1000);
                    }
                return false;
            }
        });
Sheraz Ahmad Khilji
  • 8,300
  • 9
  • 52
  • 84
25

@Saren Arterius, Using the addOnScrollListener of RecycleView, you can find the scrolling top or bottom of Vertical RecycleView like below,

RecyclerView rv = (RecyclerView)findViewById(R.id.rv);

rv.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

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

                if (dy < 0) {
                    // Recycle view scrolling up...

                } else if (dy > 0) {
                    // Recycle view scrolling down...
                }
            }
        });
TejaDroid
  • 6,561
  • 4
  • 31
  • 38
6

You can detect top by using the following

 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

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

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if(isRecyclerViewAtTop())
            {
                //your recycler view reached Top do some thing
             }
        }
    });

  private boolean isRecyclerViewAtTop()   {
        if(recyclerView.getChildCount() == 0)
            return true;
        return recyclerView.getChildAt(0).getTop() == 0;
    }

This will detect top when you release the finger once reached top, if you want to detect as soon the reacyclerview reaches top check if(isRecyclerViewAtTop()) inside onScrolled method

Manohar
  • 22,116
  • 9
  • 108
  • 144
  • 5
    This seems to be wrong. `getChildAt()` returns the first child of the `ViewGroup`, which could (and will) be different from the first element of the list. If that child happens to be perfectly aligned with the top of the recyclerview, your `IsRecyclerViewAtTop()` method will return true, even though it is not at the top. – aspyct May 29 '17 at 09:23
6

Use recyclerView.canScrollVertically(int direction) to check if top or bottom of the scroll reached.

direction = 1 for scroll down (bottom)

direction = -1 for scroll up (top)

if method return false that means you reached either top or bottom depends on the direction.

5

Just keep a reference to your layoutManager and set onScrollListener on your recycler view like this

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

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

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItemIndex = mLayoutManager.findFirstVisibleItemPosition();

        //synchronizew loading state when item count changes
        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading)
            if ((totalItemCount - visibleItemCount) <= firstVisibleItemIndex) {
                // Loading NOT in progress and end of list has been reached
                // also triggered if not enough items to fill the screen
                // if you start loading
                loading = true;
            } else if (firstVisibleItemIndex == 0){
                // top of list reached
                // if you start loading
                loading = true;
            }
        }
    }
});
Xieyi
  • 1,316
  • 1
  • 14
  • 19
SGal
  • 1,072
  • 12
  • 13
  • 1
    `setOnScrollListner` is deprecated now. – Shajeel Afzal Sep 04 '15 at 10:39
  • 2
    You should use `addOnScrollListener` because as @shajeelAfzal said `setOnScrollListener` is deprecated now , you should use `addOnScrollListener` because as @shajeelAfzal said `setOnScrollListener` is deprecated now , check [here](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29]) – VasFou Sep 08 '15 at 18:26
  • What is `previousTotal`? – CoolMind Mar 06 '19 at 07:29
3

you can do this it's work for me

      mRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            }
        }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ?
                        0 : recyclerView.getChildAt(0).getTop();
                LinearLayoutManager linearLayoutManager1 = (LinearLayoutManager) recyclerView.getLayoutManager();
                int firstVisibleItem = linearLayoutManager1.findFirstVisibleItemPosition();
                swipeRefreshLayout.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
            }
        });
Amal Kronz
  • 1,657
  • 1
  • 18
  • 22
2

I have written a RecyclerViewHelper to know the recyclerview is at top or at bottom.

public class RecyclerViewHelper {
public static boolean isAtTop(RecyclerView recyclerView) {
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        return isAtTopBeforeIceCream(recyclerView);
    } else {
        return !ViewCompat.canScrollVertically(recyclerView, -1);
    }
}

private static boolean isAtTopBeforeIceCream(RecyclerView recyclerView) {
    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    if (layoutManager instanceof LinearLayoutManager) {
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
        int pos = linearLayoutManager.findFirstVisibleItemPosition();
        if (linearLayoutManager.findViewByPosition(pos).getTop() == recyclerView.getPaddingTop() && pos == 0)
            return true;
    }
    return false;
}


public static boolean isAtBottom(RecyclerView recyclerView) {
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        return isAtBottomBeforeIceCream(recyclerView);
    } else {
        return !ViewCompat.canScrollVertically(recyclerView, 1);
    }
}

private static boolean isAtBottomBeforeIceCream(RecyclerView recyclerView) {
    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    int count = recyclerView.getAdapter().getItemCount();
    if (layoutManager instanceof LinearLayoutManager) {
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
        int pos = linearLayoutManager.findLastVisibleItemPosition();
        int lastChildBottom = linearLayoutManager.findViewByPosition(pos).getBottom();
        if (lastChildBottom == recyclerView.getHeight() - recyclerView.getPaddingBottom() && pos == count - 1)
            return true;
    }
    return false;
}

}

chefish
  • 517
  • 2
  • 7
  • 20
2

To find out if the RecyclerView is scrolled to bottom, you could reuse the methods used for the scrollbar.

This is the calculation, for convenience written as a Kotlin extension function:

fun RecyclerView.isScrolledToBottom(): Boolean {
    val contentHeight = height - (paddingTop + paddingBottom)
    return computeVerticalScrollRange() == computeVerticalScrollOffset() + contentHeight
}
Giso Bartels
  • 102
  • 1
  • 3
  • Nice solution! I saw some issues where the scroll size was only 1 off, even though I was scrolled at the bottom (probably some rounding issues) So I changed the code to use a margin: `fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight >= margin }` – Renso Lohuis Apr 06 '18 at 13:54
  • I mean, smaller or equal to margin. Like this: `fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight <= margin }` – Renso Lohuis Apr 06 '18 at 14:05
2

If you're still looking for an answer in 2022, here you go:

 mRecyclerView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
            if(!mRecyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)){
                 // reached the bottom of the list, load more data
            }
        });
OMi Shah
  • 5,768
  • 3
  • 25
  • 34
1

you can try with the OnTouchListener:

recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        if (e.getAction() == MotionEvent.ACTION_UP
            || e.getAction() == MotionEvent.ACTION_MOVE){
        if (mLinearLayoutManager.findFirstCompletelyVisibleItemPosition() > 0)
        {
        // beginning of the recycler 
        }

        if (mLinearLayoutManager.findLastCompletelyVisibleItemPosition()+1 < recyclerView.getAdapter().getItemCount())
        {
        // end of the recycler 
        }         
     }             
return false;
}
Jch Pal
  • 191
  • 2
  • 7