RecyclerView horizontal scroll snap in center RecyclerView horizontal scroll snap in center android android

RecyclerView horizontal scroll snap in center


With LinearSnapHelper, this is now very easy.

All you need to do is this:

SnapHelper helper = new LinearSnapHelper();helper.attachToRecyclerView(recyclerView);

Update

Available since 25.1.0, PagerSnapHelper can help achieve ViewPager like effect. Use it as you would use the LinearSnapHelper.

Old workaround:

If you wish for it to behave akin to the ViewPager, try this instead:

LinearSnapHelper snapHelper = new LinearSnapHelper() {    @Override    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {        View centerView = findSnapView(layoutManager);        if (centerView == null)             return RecyclerView.NO_POSITION;        int position = layoutManager.getPosition(centerView);        int targetPosition = -1;        if (layoutManager.canScrollHorizontally()) {            if (velocityX < 0) {                targetPosition = position - 1;            } else {                targetPosition = position + 1;            }        }        if (layoutManager.canScrollVertically()) {            if (velocityY < 0) {                targetPosition = position - 1;            } else {                targetPosition = position + 1;            }        }        final int firstItem = 0;        final int lastItem = layoutManager.getItemCount() - 1;        targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));        return targetPosition;    }};snapHelper.attachToRecyclerView(recyclerView);

The implementation above just returns the position next to the current item (centered) based on the direction of the velocity, regardless of the magnitude.

The former one is a first party solution included in the Support Library version 24.2.0.Meaning you have to add this to your app module's build.gradle or update it.

compile "com.android.support:recyclerview-v7:24.2.0"


Google I/O 2019 Update

ViewPager2 is here!

Google just announced at the talk 'What's New in Android' (aka 'The Android keynote') that they are working on a new ViewPager based on RecyclerView!

From the slides:

Like ViewPager, but better

  • Easy migration from ViewPager
  • Based on RecyclerView
  • Right-to-Left mode support
  • Allows vertical paging
  • Improved dataset change notifications

You can check the latest version here and the release notes here. There is also an official sample.

Personal opinion: I think this is a really needed addition. I've recently had a lot of trouble with the PagerSnapHelper oscillating left right indefinitely - see the ticket I've opened.


New answer (2016)

You can now just use a SnapHelper.

If you want a center-aligned snapping behavior similar to ViewPager then use PagerSnapHelper:

SnapHelper snapHelper = new PagerSnapHelper();snapHelper.attachToRecyclerView(recyclerView);

There is also a LinearSnapHelper. I've tried it and if you fling with energy then it scrolls 2 items with 1 fling. Personally I didn't like it, but just decide by yourself - trying it only takes seconds.


Original answer (2016)

After many hours of trying 3 different solutions found here in SO I've finally built a solution that mimics very closely the behavior found in a ViewPager.

The solution is based on the @eDizzle solution, which I believe I've improved enough to say that it works almost like a ViewPager.

Important: my RecyclerView items width is exactly the same as the screen. I haven't tried with other sizes. Also I use it with an horizontal LinearLayoutManager. I think that you will need to adapt the code if you want vertical scroll.

Here you have the code:

public class SnappyRecyclerView extends RecyclerView {    // Use it with a horizontal LinearLayoutManager    // Based on https://stackoverflow.com/a/29171652/4034572    public SnappyRecyclerView(Context context) {        super(context);    }    public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public boolean fling(int velocityX, int velocityY) {        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();        int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;        // views on the screen        int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();        View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);        int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();        View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);        // distance we need to scroll        int leftMargin = (screenWidth - lastView.getWidth()) / 2;        int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();        int leftEdge = lastView.getLeft();        int rightEdge = firstView.getRight();        int scrollDistanceLeft = leftEdge - leftMargin;        int scrollDistanceRight = rightMargin - rightEdge;        if (Math.abs(velocityX) < 1000) {            // The fling is slow -> stay at the current page if we are less than half through,            // or go to the next page if more than half through            if (leftEdge > screenWidth / 2) {                // go to next page                smoothScrollBy(-scrollDistanceRight, 0);            } else if (rightEdge < screenWidth / 2) {                // go to next page                smoothScrollBy(scrollDistanceLeft, 0);            } else {                // stay at current page                if (velocityX > 0) {                    smoothScrollBy(-scrollDistanceRight, 0);                } else {                    smoothScrollBy(scrollDistanceLeft, 0);                }            }            return true;        } else {            // The fling is fast -> go to next page            if (velocityX > 0) {                smoothScrollBy(scrollDistanceLeft, 0);            } else {                smoothScrollBy(-scrollDistanceRight, 0);            }            return true;        }    }    @Override    public void onScrollStateChanged(int state) {        super.onScrollStateChanged(state);        // If you tap on the phone while the RecyclerView is scrolling it will stop in the middle.        // This code fixes this. This code is not strictly necessary but it improves the behaviour.        if (state == SCROLL_STATE_IDLE) {            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();            int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;            // views on the screen            int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();            View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);            int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();            View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);            // distance we need to scroll            int leftMargin = (screenWidth - lastView.getWidth()) / 2;            int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();            int leftEdge = lastView.getLeft();            int rightEdge = firstView.getRight();            int scrollDistanceLeft = leftEdge - leftMargin;            int scrollDistanceRight = rightMargin - rightEdge;            if (leftEdge > screenWidth / 2) {                smoothScrollBy(-scrollDistanceRight, 0);            } else if (rightEdge < screenWidth / 2) {                smoothScrollBy(scrollDistanceLeft, 0);            }        }    }}

Enjoy!


If the goal is to make the RecyclerView mimic the behavior of ViewPager there is quite easy approach

RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);SnapHelper snapHelper = new PagerSnapHelper();recyclerView.setLayoutManager(layoutManager);snapHelper.attachToRecyclerView(mRecyclerView);

By using PagerSnapHelper you can get the behavior like ViewPager