RecyclerView horizontal scroll snap in center

snaphelper recyclerview github
snaphelper scroll to position
how to set item to center of recyclerview when selected
how to scroll horizontal recyclerview programmatically
pagersnaphelper recyclerview android
get center visible item of recyclerview when scrolling
recyclerview scroll to position center
recyclerview scroll one item at a time

I'm trying to make a carousel-like view here using RecyclerView, I want the item to snap in the middle of the screen when scrolling, one item at a time. I've tried using recyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);

but the view is still scrolling smoothly, I've also tried to implement my own logic using scroll listener like so:

recyclerView.setOnScrollListener(new OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    Log.v("Offset ", recyclerView.getWidth() + "");
                    if (newState == 0) {
                        try {
                               recyclerView.smoothScrollToPosition(layoutManager.findLastVisibleItemPosition());
                                recyclerView.scrollBy(20,0);
                            if (layoutManager.findLastVisibleItemPosition() >= recyclerView.getAdapter().getItemCount() - 1) {
                                Beam refresh = new Beam();
                                refresh.execute(createUrl());
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

The swipe from right to left is working fine now, but not the other way around, what am I missing here ?

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"

Detecting snap changes with Android's RecyclerView SnapHelper , Both support horizontal and vertical orientations. LinearSnapHelper is intended for smaller items and snaps the center of the target child view to the center of We know that the snap position will only change during scrolling. The following is a reworked onSetRecyclerView() that will snap the central item of the RecyclerView to the center of the screen. Notice that the LinearSnapHelper is not attached until the RecyclerView is laid out and scrolled appropriately. You do not need to do any scrolling in onLayoutChildren().

Snap-to-Center RecyclerView Extension � GitHub, public class SnappyRecyclerView extends RecyclerView {. // Use it with a horizontal LinearLayoutManager. // Based on distance we need to scroll. @sativa "The implementation will snap the center of the target child view to the center of the attached RecyclerView. If you intend to change this behavior then override calculateDistanceToFinalSnap(RecyclerView.LayoutManager, View)." – Jake Jan 30 '18 at 21:08

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

RecyclerView horizontal scroll snap in center, I'm trying to make a carousel-like view here using RecyclerView, I want the item to snap in the middle of the screen when scrolling, one item at a time. The onTargetFound method triggers once this view becomes a child of the RecyclerView. We need to specify how long the smooth scroller should take to snap to this view. It also provides us a way to define the upper limit for how long the scroll action should take (see MAX_SCROLL_ON_FLING_DURATION_MS).

You need to use findFirstVisibleItemPosition for going in the opposite direction. And for detecting which direction the swipe was in, youll need to get either the fling velocity or the change in x. I approached this problem from a slightly different angle than you have.

Create a new class that extends the RecyclerView class and then override RecyclerView's fling method like so:

@Override
public boolean fling(int velocityX, int velocityY) {
    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();

//these four variables identify the views you see on screen.
    int lastVisibleView = linearLayoutManager.findLastVisibleItemPosition();
    int firstVisibleView = linearLayoutManager.findFirstVisibleItemPosition();
    View firstView = linearLayoutManager.findViewByPosition(firstVisibleView);
    View lastView = linearLayoutManager.findViewByPosition(lastVisibleView);

//these variables get the distance you need to scroll in order to center your views.
//my views have variable sizes, so I need to calculate side margins separately.     
//note the subtle difference in how right and left margins are calculated, as well as
//the resulting scroll distances.
    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(user swipes to the left) 
    if(velocityX > 0) smoothScrollBy(scrollDistanceLeft, 0);
    else smoothScrollBy(-scrollDistanceRight, 0);

    return true;
}

RecyclerView horizontal scroll snap in center, I'm trying to make a carousel-like view here using RecyclerView, I want the item to snap in the middle of the screen when scrolling, one item at a time. I've tried� As you can see, the RecyclerView snaps to the first item in the adapter. If you use the default LinearSnapHelper, you can only snap to the center. The only code needed is: SnapHelper snapHelper = new LinearSnapHelper (); snapHelper. attachToRecyclerView (recyclerView); This produces the following result:

Just add padding and margin to recyclerView and recyclerView item:

recyclerView item:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parentLayout"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_marginLeft="8dp" <!-- here -->
    android:layout_marginRight="8dp" <!-- here  -->
    android:layout_width="match_parent"
    android:layout_height="200dp">

   <!-- child views -->

</RelativeLayout>

recyclerView:

<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp" <!-- here -->
    android:paddingRight="8dp" <!-- here -->
    android:clipToPadding="false" <!-- important!-->
    android:scrollbars="none" />

and set PagerSnapHelper :

int displayWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
parentLayout.getLayoutParams().width = displayWidth - Utils.dpToPx(16) * 4;
SnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

dp to px:

public static int dpToPx(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}

result:

RecyclerView Recipes: Snap to first item, But how do you customize that behavior to snap to the first? Our SnapHelper implementation will assume a Horizontal RecyclerView . But you Override this method to limit the maximum scroll distance from a fling. On Medium, smart voices and original ideas take center stage - with no ads in sight. For example, you can snap the firstVisibleItem of the RecyclerView as you must have seen in the play store application that the firstVisibleItem will be always completely visible when scrolling comes to the idle position. Android already provides a helper class to do the same. The class is LinearSnapHelper which will only enable the center snapping.

Using SnapHelper in RecyclerView, For example, you can snap the firstVisibleItem of the RecyclerView as you will be always completely visible when scrolling comes to the idle position. The class is LinearSnapHelper which will only enable the center snapping. distanceToStart(targetView, getHorizontalHelper(layoutManager)); } else� I have 3 items in my horizontal RecyclerView and I'm setting scaleUnfocusedViews to true. Upon first scrolling, the first and last item will be redrawn to have gaps (left and right) and it seems like the width is squeezed. However the items between it remains the same, the only issue is, it loses the original margin set in the xml.

RecyclerView snapping with SnapHelper, If you use the default LinearSnapHelper, you can only snap to the center. I'm using 2 horizontal recyclerviews (one below the other) that scroll together. The following examples show how to use androidx.recyclerview.widget.LinearSnapHelper.These examples are extracted from open source projects. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.

Recyclerview snap in center, [x] supports horizontal and vertical scrolling. RecyclerView horizontal scroll snap in center (5) If the goal is to make the RecyclerView mimic the� SnapHelper was a great addition to the AndroidX (previously Support Library) RecyclerView package. In short, it can be used to change the behavior of a RecyclerView such that items snap to discrete…

Comments
  • This did not work for me (but the solution by @Albert Vila Calvo did). Have you implemented it? Are we supposed to override methods from it? I would think that the class should do all of the work out of the box
  • Yes, hence the answer. I have not tested thoroughly however. To be clear, I did this with a horizontal LinearLayoutManager where the views all were regular in size. Nothing else but the snippet above is needed.
  • @galaxigirl apologies, I have understood what you meant and have made some edit acknowledging that, please do comment. This is an alternate solution with the LinearSnapHelper.
  • This worked just right for me. @galaxigirl overriding this method helps in that the linear snap helper will otherwise snap to the nearest item when scroll is settling, not exactly just the next/previous item. Thanks a lot for this, razzledazzle
  • LinearSnapHelper was the initial solution that worked for me, but it seems that PagerSnapHelper gave the swiping a better animation.
  • Nice catch the on onScrollStateChanged(); otherwise, a small bug is present if we swipe slowly the RecyclerView. Thanks!
  • To support margins (android:layout_marginStart="16dp") I tried with int screenwidth = getWidth(); and it seems to work.
  • AWESOOOMMEEEEEEEEE
  • @galaxigirl no, I haven't tryied LinearSnapHelper yet. I just updated the answer so that people knows about the official solution. My solution works without any issue on my app.
  • @AlbertVilaCalvo I tried LinearSnapHelper. LinearSnapHelper works with a Scroller. It scrolls and snaps based on the gravity and the fling velocity. Higher the speed more pages are scrolled. I wouldn't recommend it for a ViewPager like behaviour.
  • I am using LinearSnapHelper instead of PagerSnapHelper and it works for me
  • Yes, there is no snap effect in LinearSnapHelper