275

I'm looking for a way to scroll a RecyclerView to show the selected item on top.

In a ListView I was able to do that by using scrollTo(x,y) and getting the top of the element that need to be centered.

Something like:

@Override public void onItemClick(View v, int pos){ mylistView.scrollTo(0, v.getTop()); } 

The problem is that the RecyclerView returns an error when using it's scrollTo method saying

RecyclerView does not support scrolling to an absolute position

How can I scroll a RecyclerView to put the selected item at the top of the view?

19 Answers 19

480

If you are using the LinearLayoutManager or Staggered GridLayoutManager, they each have a scrollToPositionWithOffset method that takes both the position and also the offset of the start of the item from the start of the RecyclerView, which seems like it would accomplish what you need (setting the offset to 0 should align with the top).

For instance:

//Scroll item 2 to 20 pixels from the top linearLayoutManager.scrollToPositionWithOffset(2, 20); 
Sign up to request clarification or add additional context in comments.

11 Comments

If anyone needs this for restoring the scroll position of a RecyclerView, this is how to save the scroll positions (the two arguments for the method scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition(); View v = linearLayoutManager.getChildAt(0); int top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
What I don't seem to get about this is when to call scrollToPosition? if the selection listener is on the individual views, how will the RecyclerView object (which has access to its layout manager that can call scrolToPosition) be notified that an item is selected?
@Nonos you can first store the LayoutManager that you use in RecylerView.setLayoutManager() and then access it directly. Like this: LinearLayoutManager llm = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(llm); ...(later in code)... llm.scrollToPositionWithOffset(2, 20);
What if I want to scroll to the top "smoothly"?
@Tiago, I haven't played around with it, but this 3-part series of articles may help: blog.stylingandroid.com/scrolling-recyclerview-part-1
|
111
+50

If you looking for vertical LinearLayout Manager you can achieve smooth scrolling using a custom LinearSmoothScroller:

import android.content.Context; import android.graphics.PointF; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; public class SnappingLinearLayoutManager extends LinearLayoutManager { public SnappingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()); smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); } private class TopSnappedSmoothScroller extends LinearSmoothScroller { public TopSnappedSmoothScroller(Context context) { super(context); } @Override public PointF computeScrollVectorForPosition(int targetPosition) { return SnappingLinearLayoutManager.this .computeScrollVectorForPosition(targetPosition); } @Override protected int getVerticalSnapPreference() { return SNAP_TO_START; } } } 

use an instance of the layoutmanager in recycle view and then calling recyclerView.smoothScrollToPosition(pos); will smooth scroll to selected position to top of the recycler view

7 Comments

Really useful answer! I also override getHorizontalSnapPreference() in the TopSnappedSmoothScroller for Horizontal lists.
Am I right in observing that this only works only for items that still have enough after/below them to fill the remaining RecyclerView? Which is to say: This does not seem to work for the last item - regardless of the SNAP_TO_START the RecyclerView only scrolls until the item becomes visible at the bottom but does not move it all the way up. Any ideas of how to achieve this? (I have worked around this by setting custom padding on the RecyclerView but that does not seem really right...)
sometime the item dosent get focused, i am having a focus drawable for item view.
@david.mihola Have you found a 'right' way? I've set custom padding to my recyclerview but I'm having some weird problems...
|
69

//Scroll item pos

linearLayoutManager.scrollToPositionWithOffset(pos, 0); 

3 Comments

is there any way to do this smoothly?
@MarkBuikema I found it before. thanx for your attention dude
29

You just need to call recyclerview.scrollToPosition(position). That's fine!

If you want to call it in adapter, just let your adapter has the instance of recyclerview or the activity or fragment which contains recyclerview,than implements the method getRecyclerview() in them.

I hope it can help you.

5 Comments

This worked for me - recyclerView.scrollToPosition(0); put me at the top.
that not working you have to use the linearLayoutManager.scrollToPositionWithOffset(pos, 0);
@Anil Ugale That's nonsense. Of course it works. You can use any LayoutManager for a RecyclerView. Why should you later care about it when you want to scroll? I think you were doing something wrong. Recyclerview.scrollToPosition(position) is a convenience method which calls LayoutManager.scrollToPosition(position).
@TheincredibleJan it doesn't work in all cases, the layoutManager is generally responsible for the scrolling anyhow just for your information.
This doesn't always put the requested item on the top as the question asked.
28

If you want to scroll automatic without show scroll motion then you need to write following code:

mRecyclerView.getLayoutManager().scrollToPosition(position); 

If you want to display scroll motion then you need to add following code. =>Step 1: You need to declare SmoothScroller.

RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(this.getApplicationContext()) { @Override protected int getVerticalSnapPreference() { return LinearSmoothScroller.SNAP_TO_START; } }; 

=>step 2: You need to add this code any event you want to perform scroll to specific position. =>First you need to set target position to SmoothScroller.

smoothScroller.setTargetPosition(position); 

=>Then you need to set SmoothScroller to LayoutManager.

mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller); 

1 Comment

Amazing! The simplest solution is if you need a smooth scroll with the target element showing at the top of the RecyclerView! Thanks!
13

just call this method simply:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0); 

instead of:

recyclerView.scrollToPosition(yourItemPosition); 

2 Comments

"instead of"? Distwo didn't mention "scrollToPosition". If he had used it there wouldn't have been any problem. scrollToPosition() as intended.
strangely enough, using scrollToPositionWithOffset works for me and recyclerview.scrollToPosition didnt. Although , i would request Amir to change the RecyclerView to recyclerview, because it gives a sense that the methods are static, and they are not.
11

same with speed regulator

public class SmoothScrollLinearLayoutManager extends LinearLayoutManager { private static final float MILLISECONDS_PER_INCH = 110f; private Context mContext; public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) { super(context,orientation,reverseLayout); mContext = context; } @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){ //This controls the direction in which smoothScroll looks for your view @Override public PointF computeScrollVectorForPosition(int targetPosition) { return new PointF(0, 1); } //This returns the milliseconds it takes to scroll one pixel. @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return MILLISECONDS_PER_INCH / displayMetrics.densityDpi; } }; smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); } private class TopSnappedSmoothScroller extends LinearSmoothScroller { public TopSnappedSmoothScroller(Context context) { super(context); } @Override public PointF computeScrollVectorForPosition(int targetPosition) { return SmoothScrollLinearLayoutManager.this .computeScrollVectorForPosition(targetPosition); } @Override protected int getVerticalSnapPreference() { return SNAP_TO_START; } } } 

Comments

11

What i may add here is how to make it work together with DiffUtil and ListAdapter

You may note that calling recyclerView.scrollToPosition(pos) or (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset) wouldn't work if called straight after adapter.submitList. It is because the differ looks for changes in a background thread and then asynchronously notifies adapter about changes. On a SO i have seen several wrong answers with unnecessary delays & etc to solve this.

To handle the situation properly the submitList has a callback which is invoked when changes have been applied.

So the proper kotlin implementations in this case are:

//memorise target item here and a scroll offset if needed adapter.submitList(items) { val pos = /* here you may find a new position of the item or just use just a static position. It depends on your case */ recyclerView.scrollToPosition(pos) } //or adapter.submitList(items) { recyclerView.smoothScrollToPosition(pos) } //or etc adapter.submitList(items) { (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset) } 

2 Comments

You mentioned great point: "changes happens in a background thread and then asynchronously notifies adapter about changes". TNX.
Do we update the scroll position from Runnable?
9

Try what worked for me cool!

Create a variable private static int displayedposition = 0;

Now for the position of your RecyclerView in your Activity.

myRecyclerView.setOnScrollListener(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); LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager(); displayedposition = llm.findFirstVisibleItemPosition(); } }); 

Place this statement where you want it to place the former site displayed in your view .

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager(); llm.scrollToPositionWithOffset(displayedposition , youList.size()); 

Well that's it , it worked fine for me \o/

Comments

8

what i did to restore the scroll position after refreshing the RecyclerView on button clicked:

if (linearLayoutManager != null) { index = linearLayoutManager.findFirstVisibleItemPosition(); View v = linearLayoutManager.getChildAt(0); top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop()); Log.d("TAG", "visible position " + " " + index); } else{ index = 0; } linearLayoutManager = new LinearLayoutManager(getApplicationContext()); linearLayoutManager.scrollToPositionWithOffset(index, top); 

getting the offset of the first visible item from the top before creating the linearLayoutManager object and after instantiating it the scrollToPositionWithOffset of the LinearLayoutManager object was called.

Comments

8

I don't know why I didn't find the best answer but its really simple.

recyclerView.smoothScrollToPosition(position); 

No errors

Creates Animations

2 Comments

where will you put this in activity or adapter?
This won't scroll clicked item to the top - which was the problem in the first place.
5

Introduction

None of the answers explain how to show last item(s) at the top. So, the answers work only for items that still have enough items above or below them to fill the remaining RecyclerView. For instance, if there are 59 elements and a 56-th element is selected it should be at the top as in the picture below:

example

So, let's see how to implement this in the next paragraph.

Solution

We could handle those cases by using linearLayoutManager.scrollToPositionWithOffset(pos, 0) and additional logic in the Adapter of RecyclerView - by adding a custom margin below the last item (if the last item is not visible then it means there's enough space fill the RecyclerView). The custom margin could be a difference between the root view height and the item height. So, your Adapter for RecyclerView would look as follows:

... @Override public void onBindViewHolder(ViewHolder holder, final int position) { ... int bottomHeight = 0; int itemHeight = holder.itemView.getMeasuredHeight(); // if it's the last item then add a bottom margin that is enough to bring it to the top if (position == mDataSet.length - 1) { bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight); } RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams(); params.setMargins(0, 0, params.rightMargin, bottomHeight); holder.itemView.setLayoutParams(params); ... } ... 

Comments

4

If your LayoutManager is LinearLayoutManager you can use scrollToPositionWithOffset(position,0); on it and it will make your item the first visible item in the list. Otherwise, you can use smoothScrollToPosition on the RecyclerView directly.

I ended up using the below code.

 RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { // Scroll to item and make it the first visible item of the list. ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0); } else { mainList.smoothScrollToPosition(position); } 

Comments

3

scroll at particular position
and this helped me alot. by click listener you can get the position in your adapter

layoutmanager.scrollToPosition(int position); 

Comments

2

In my case my RecyclerView have a padding top like this

<android.support.v7.widget.RecyclerView ... android:paddingTop="100dp" android:clipToPadding="false" /> 

Then for scroll a item to top, I need to

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop()); 

Comments

2

This is pretty simple

recyclerView.scrollToPosition(position) 

3 Comments

but this will not scroll to the top of the screen it can be in middle in bottom it will just show selected position item
If you want top use recyclerView.scrollToPosition(0)
I have 10 items and i click on some button and i need to scroll to item 7 if you write recyclerview.scrollToPosition(7) it will scroll that position but if i want that item to display on the top of the screen then?
1

please note that if scrollToPosition not work notice that your RecyclerView was inside a NestedScrollView; refer to this post

Comments

1

If you've Recycler view inside nestedscrollview :

val y = recyclerview.getChildAt(0).y recyclerview.smoothScrollTo(0, y.toInt()) 

If your Recycler view is not inside nestedscrollview :

recyclerview.smoothScrollToPosition(index) 

or

recyclerview.layoutManager?.smoothScrollToPosition(recyclerview, null ,index) 

Comments

-2

I use the code below to smooth-scroll an item (thisView) to the top.
It works also for GridLayoutManager with views of different heights:

View firstView = mRecyclerView.getChildAt(0); int toY = firstView.getTop(); int firstPosition = mRecyclerView.getChildAdapterPosition(firstView); View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition); int fromY = thisView.getTop(); mRecyclerView.smoothScrollBy(0, fromY - toY); 

Seems to work good enough for a quick solution.

3 Comments

What is value of thisPosition ?
it's the position you want to smooth scroll to
Why not simply use smoothScrollToPosition(int position) without any calculation? Doesn't matter from where you start, does it?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.