53

How to get the scrollposition in Recyclerview or the Layoutmanager?

I can measure the scrollposition by adding an OnScrollListener, but when the Orientation changes scrollDy is 0.

    mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            int scrollDy = 0;
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                scrollDy += dy;
            }
        });
seb
  • 4,279
  • 2
  • 25
  • 36
  • 3
    Can you improve the quality of your question a bit? Your answer looks good, but the question needs to state the problem being solved more clearly. – Robert Harvey Apr 11 '15 at 18:54
  • This https://stackoverflow.com/questions/36568168/how-to-save-scroll-position-of-recyclerview-in-android/36569778#3656977 is the correct answer. Have a look, it worked for me. – Rahul Sahni Oct 07 '17 at 20:11

7 Answers7

66

For future use, If you are switching between Fragments within the same activity and all you want to do is save scroll-position for recyclerview and then restore recyclerview to the same scroll-position, you can do as follows:

In your onStop()/onDestroyView()/onPause() whatever callback is more appropriate, do this:

Parcelable recylerViewState = recyclerView.getLayoutManager().onSaveInstanceState();

And In your onStart()/onCreateView()/onResume() whatever callback is more appropriate, do this:

recyclerView.getLayoutManager().onRestoreInstanceState(recylerViewState);

This way you can successfully keep your recyclerView's state in a parcelable and restore it whenever you want.

Shoaib Anwar
  • 1,555
  • 12
  • 26
55

You cannot get it because it does not really exist. LayoutManager only knows about the views on screen, it does not know the views before, what their size is etc.

The number you can count using the scroll listener is not reliable because if data changes, RecyclerView will do a fresh layout calculation and will not try to re-calculate a real offset (you'll receive an onScroll(0, 0) if views moved).

RecyclerView estimates this value for scrollbars, you can use the same methods from View class.

computeHorizontalScrollExtent

computeHorizontalScrollRange

computeHorizontalScrollOffset

These methods have their vertical counterparts as well.

TheIT
  • 11,919
  • 4
  • 64
  • 56
yigit
  • 37,683
  • 13
  • 72
  • 58
  • 1
    Hello yigit again :) I'm the card guy that mailed you last time. So, I want to move views across the screen like here: https://github.com/ksoichiro/Android-ObservableScrollView/blob/master/samples/images/demo12.gif I am able to do it, by using the method above. But after an orientation change this field is 0. How could a reliable solution look like? Because your answer suggests also that storing the scrollposition in a savedstate wouldn't make things better, true? – seb Apr 11 '15 at 20:28
  • I think your best option is to recover your scroll position after a rotate. You can get first visible position and calculate your scroll position depending on that. You can also traverse children of layout manager, LLM guarantees that child position order will match adapter order. This is not an API guarantee but very very unlikely to change. – yigit Apr 11 '15 at 23:06
  • It seems like all of these methods return 0, the default implementation. You are expected to implement this method in the layout manager. – Andrew Orobator Apr 29 '16 at 15:20
  • ScrollOffset is working for me… but when I go to another activity and come back, the offset is becoming 0 for all posible scrolls… I'm storing the state of layout manager using savedinstancestate which is working good… is there anything else I need to store…? – Sourav Roy Jan 05 '17 at 03:34
43

I came to the question just wanting to get the item position index that is currently scrolled to. For others who want to do the same, you can use the following:

LinearLayoutManager myLayoutManager = myRecyclerView.getLayoutManager();
int scrollPosition = myLayoutManager.findFirstVisibleItemPosition();

You can also get these other positions:

  • findLastVisibleItemPosition()
  • findFirstCompletelyVisibleItemPosition()
  • findLastCompletelyVisibleItemPosition()

Thanks to this answer for help with this. It also shows how to save and restore the scroll position.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
17

recyclerView.computeVerticalScrollOffset() does the trick.

adiga
  • 34,372
  • 9
  • 61
  • 83
Johann
  • 27,536
  • 39
  • 165
  • 279
  • I haven't yet run into a scenario where this doesn't work, but technically this method is "the vertical offset of the vertical scrollbar's thumb", not the scroll (https://developer.android.com/reference/androidx/core/view/ScrollingView#computeVerticalScrollOffset()) – Ed Lee Dec 19 '20 at 03:17
  • Are you sure? "Default implementation returns 0." – Jeffrey Blattman Oct 07 '21 at 20:02
2

Found a work around to getting last scrolled position within the RecyclerView with credits to Suragch solution... Declare a globally accessible LinearLayoutManager so a to be able to access it within inner methods... When ever the populateChatAdapter method is called, if its the first call the scroll to position will be the last sent message and if the user had scrolled to view previous messages and method is called again to update new messages then they will retain their last scrolled to position...

LinearLayoutManager linearLayoutManager;
int lastScrollPosition = 0;
RecyclerView messages_recycler;

Initiate them...

messages_recycler = findViewById(R.id.messages_recycler);
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
messages_recycler.setLayoutManager(linearLayoutManager);

On your populate adapter method

private void populateChatAdapter(ObservableList<ChatMessages> msgs) {

    if (lastScrollPosition==0) lastScrollPosition = msgs.size()-1;
    ChatMessagesRecycler adapter = new ChatMessagesRecycler(msgs);
    messages_recycler.setAdapter(adapter);
    messages_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            lastScrollPosition = linearLayoutManager.findLastCompletelyVisibleItemPosition();
        }
    });
    messages_recycler.scrollToPosition(lastScrollPosition);
}
0

You have to read scrollDy in

public void onScrollStateChanged(RecyclerView recyclerView, int newState)

then you can get recyclerView's scroll position

Ian
  • 30,182
  • 19
  • 69
  • 107
-4

To get ScroolPosition try this:

  1. You will need this:

        mLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);
    
  2. And then get the position of the mLayoutManager (like this):

 recyclerView
            .addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    int pos = mLayoutManager.getPosition(v);
                    RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(pos);
         }
});

With this "pos" you get the position, startin with the 0.

   int pos = mLayoutManager.getPosition(v);