Inside RecyclerView’s SnapHelper
Trying to make your RecyclerView snap to a particular item? Try using SnapHelper. See how you can customize it to make it work for you!

RecyclerView is the evolution to ListView. It brings a powerhouse of features. We can make it behave like a carousel or have it snap to a specific position. But manually doing this is difficult. That’s where Android’s support library has us covered. SnapHelper
makes it a breeze to do this, but what about when we need to customize some of the details? This article takes a deeper look at the internals of the SnapHelper
so you can tweak and make it work for you.
What is SnapHelper
So you need to augment your RecyclerView
to snap to the first visible item in the list. Or maybe you need it to snap to the item closest to the middle. Or maybe some other variation. One option might be to attach a RecyclerView.OnFlingListener
and intercept the events. That’s a lot of work and easy to overlook some things.
This is where SnapHelper
comes in. The SnapHelper
component is an abstract class. Android offers two variants for you: LinearSnapHelper
and PagerSnapHelper
. They offer pretty much all you need, but if it’s not enough, you should consider extending one of them.LinearSnapHelper
snaps to the item that is closest to the middle of the RecyclerView
.PagerSnapHelper
offers similar behavior to a ViewPager
but requires that your item views have their layout parameters set to MATCH_PARENT
.
Using the SnapHelper in your project
Integrating one of the concrete SnapHelper
implementations is very easy.
Let’s take a deeper look at SnapHelper
's components.
Components
SnapHelper
implements theRecyclerView.OnFlingListener
.LinearSnapHelper
orPagerSnapHelper
are concrete implementations ofSnapHelper
. They offer most of the functionality needed to implement your customSnapHelper
. Consider extending one of these to suit your needs. For example,LinearSnapHelper
snaps to the view closest to the middle of the parent.Scroller
provides a way to calculate the distanceRecyclerView
needs to scroll to the target view.RecyclerView.SmoothScroller
performs the act of smooth scrolling to a target view.LinearSmoothScroller
extendsSmoothScroller
and will smooth scroll to the target view using a linear interpolator. The interpolator is switched to a decelerate interpolator once it approaches the target view.
How it works
- We construct our
SnapHelper
and attach it to aRecyclerView
:LinearSnapHelper().attachToRecyclerView(recyclerView)
TheSnapHelper
creates a newOnScrollListener
andOnFlingListener
and registers them with theRecyclerView
. It also constructs a newScroller
. TheScroller
calculates the distanceRecyclerView
needs to reach the target view. RecyclerView.fling(int velocityX, int velocityY)
captures the fling velocity and forwards it toSnapHelper
. Recall thatSnapHelper
implementsRecyclerView.OnFlingListener
and registered itself with theRecyclerView
.SnapHelper
’sonFling(int velocityX, int velocityY)
method is called. We check if the velocity in either x or y exceeds the minimum fling velocity. If it does thensnapFromFling
is invoked.snapFromFling
will do all the work to smooth scroll and snap to the target position. This requires calling a few methods to do the heavy lifting. First, aLinearSmoothScroller
instance is created (by a call tocreateSnapScroller
). Then theSnapHelper
finds the target position in the adapter to snap to for the givenx
andy
velocities (by a call tofindTargetSnapPosition
). This position is passed to theLinearSmoothScroller
:smoothScroller.setTargetPosition(targetPosition)
. Finally, theSnapHelper
tellsLayoutManager
to smooth scroll to the target view:
layoutManager.startSmoothScroll(smoothScroller);
When everything is complete, the target view will be snapped in to position on screen.

Tip
Need to limit the maximum fling distance? Here’s how:
What the SmoothScroller does
The RecyclerView
provides an abstract class: RecyclerView.SmoothScroller
. The smooth scroller provides primitive capabilities to track the target view position and trigger scrolling programatically. Android provides a concrete implementation of this: LinearSmoothScroller
. This class uses a LinearInterpolator
. It switches to aDecelerateInterpolator
once the target view becomes a child of the RecyclerView
. This gives the impression that the RecyclerView
slowly approaches the target view.
There’s 3 methods of particular importance:
calculateSpeedPerPixel
: Easiest and simplest one where we convert display metrics to pixels per secondreturn MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
calculateTimeForScrolling
: The default doesn’t place an upper bound on time but does ensure that if the amount scrolled is very small that it always returns a positive time (avoiding rounding errors).(int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX);
onTargetFound
: Called when the target view becomes a child of theRecyclerView
. This is the last callback received by theSmoothScroller
. We need to calculate the distance and time to the snap position.calculateDistanceToFinalSnap
will provide the scroll distance (SnapHelper
defines this as an abstract method).calculateTimeForDeceleration
will provide the time given the distance computed above.Action
object is updated with the time and distance details.LinearSmoothScroller
uses this information to complete the scroll.
Tip
Need to limit the time a smooth scroll can take? Here’s how:
How to find the target adapter position
The SnapHelper
needs a way of translating the fling velocity to an item position in the adapter. In order to do this, you need to implement findTargetSnapPosition(LayoutManager layoutManager, int velocityX, int velocityY)
. Note: This doesn’t have to be accurate — treat it as more of an estimate.
To get a better understanding of how to do this, let’s take a deeper look at how LinearSnapHelper
determines the target position. Recall that LinearSnapHelper
's goal is to snap to the item that is closest to the center.
First, it finds the current center view and its position in the adapter. Next, with the help of the Scroller
, the LinearSnapHelper
calculates the distance the RecyclerView
should scroll – using calculateScrollDistance(int velocityX, int velocityY)
. We need to map the total scroll distance to a number of adapter items. LinearSnapHelper
accomplishes this by calculating the average RecyclerView
child height currently visible. This represents the average amount to scroll per child. Now we can estimate the total number of children to scroll:val delta = totalScrollDistance / avgScrollDistancePerChild
.
Finally, the new target snap position is estimated as currentCenterPos + delta
.

One interesting thing to note here is that the direction to scroll in comes from the velocity. However, this may not match the order of children in the LayoutManager
. To overcome this, a ScrollVectorProvider
is used to get the direction (Note: All LayoutManager
s provided by Android implement theScrollVectorProvider
):
val vectorProvider = layoutManager as RecyclerView.SmoothScroller.ScrollVectorProvider
val vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1)
If we assume that we are scrolling vertically then the value in vectorForEnd
allows us to define the direction as:
if (vectorForEnd.y < 0) {
delta = -delta;
}
val targetPos = Math.max(0, Math.min(itemCount - 1, currentPos + delta))
How to find the target snap view
Once the RecyclerView
begins to settle, we need to find a reference view to snap to from the current set of child views. This is the purpose of findSnapView(LayoutManager layoutManager)
. Let’s take a deeper look at how LinearSnapHelper
determines the target snap view. LinearSnapHelper
walks over the children currently attached to the RecyclerView
. It keeps track of the view that is closest to the center. At the end, it returns the view which it found to be closest to the center.
Creating a widget that snaps to a particular position was once a very difficult. Accomplishing this with a ListView
was extremely difficult. RecyclerView
made it easier but there was still a tremendous amount of heavy lifting. With SnapHelper
, and it’s two concrete implementations — LinearSnapHelper
and PagerHelper
, this becomes painless and simple. Extending their functionality is also simple. Typically this requires modifying findTargetSnapPosition
, findSnapView
, or calculateDistanceToFinalSnap
. We looked at the details of SnapHelper
’s components and how they interact with each other. This knowledge should aid in extending SnapHelper
to tailor it to your needs. Share your tips and how you use SnapHelper
!