7

I'm using a LinearSnapHelper to make items in my RecyclerView "snap" into place on the screen (my cards take up most of the screen, so I want them to snap into place and fill the screen on every swipe/fling/scroll).

I'm struggling with how to make the cards snap into place faster. I've tried creating a custom LinearLayoutManager (and editing the calculateSpeedPerPixel method in scrollToPosition or smoothScrollToPosition), as well as a custom RecyclerView (and editing the fling method). But nothing effects the speed that cards "snap" into place.

I suppose the issue is that I don't really understand how LinearSnapHelper "scrolls" the cards into position. It doesn't seem to use LinearLayoutManager's scrollToPosition or smoothScrollToPosition methods.

    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(mRecyclerView);
Sebastian Kaczmarek
  • 7,135
  • 4
  • 17
  • 36

5 Answers5

6

As 郭玉龙 mentioned, SnapHelper call RecyclerView.smoothScrollBy() method. And it use default sQuinticInterpolator. To change speed of snap you can do next:

public class SlowdownRecyclerView extends RecyclerView {

// Change pow to control speed.
// Bigger = faster. RecyclerView default is 5.
private static final int POW = 2;

private Interpolator interpolator;

public SlowdownRecyclerView(Context context) {
    super(context);
    createInterpolator();
}

public SlowdownRecyclerView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    createInterpolator();
}

public SlowdownRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    createInterpolator();
}

private void createInterpolator(){
    interpolator = new Interpolator() {
        @Override
        public float getInterpolation(float t) {
            t = Math.abs(t - 1.0f);
            return (float) (1.0f - Math.pow(t, POW));
        }
    };
}

@Override
public void smoothScrollBy(int dx, int dy) {
    super.smoothScrollBy(dx, dy, interpolator);
}

Or you can implement your own interpolator.

devilbrain666
  • 71
  • 1
  • 4
3

The speed of snapping scroll is affected by RecyclerView.smoothScrollBy().

Here's the snippet of source code.

Picture

Override this function to increase or decrease the speed of snapping scroll.

Abu Yousuf
  • 4,421
  • 2
  • 23
  • 39
郭玉龙
  • 61
  • 9
2

I wound up doing this by adding a ScrollListener to my RecycleView, and then creating a custom LinearLayoutManager and custom smoothScrollToPosition method.

    final CustomLinearLayoutManager mLayoutManager = new CustomLinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(mLayoutManager);

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        private boolean scrollingUp;

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            scrollingUp = dy < 0;
        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                int visiblePosition = scrollingUp ? mLayoutManager.findFirstVisibleItemPosition() : mLayoutManager.findLastVisibleItemPosition();
                int completelyVisiblePosition = scrollingUp ? mLayoutManager
                        .findFirstCompletelyVisibleItemPosition() : mLayoutManager
                        .findLastCompletelyVisibleItemPosition();
                if (visiblePosition != completelyVisiblePosition) {
                    recyclerView.smoothScrollToPosition(visiblePosition);
                    return;
                }
        }
    });
  • why does your linear layout as to be custom, have you tried it with standard horizontal linear layout ? – 2cupsOfTech May 23 '17 at 11:07
  • You need the CustomLinearLayoutManager to speed up the snap/scroll speed. Here is an example https://stackoverflow.com/a/36784136/ – Jerry Sha Jan 04 '19 at 19:29
0

I achieved this using a library https://github.com/rubensousa/GravitySnapHelper

you can also override findTargetSnapPosition to get pager like scroll

tweek the scrollMsPerInch to increase / decrease speed

    val snapHelper : GravitySnapHelper = GravitySnapHelper(Gravity.CENTER)
    
    // the lower the higher the speed, default is 100f
    snapHelper.scrollMsPerInch = 40f

    snapHelper.attachToRecyclerView(binding?.mRecyclerView)
-1

Actually you can modify the LinearSnapHelper and SnapHelperClass by simply copy/paste the existing code the only thing you will do is to set MILLISECONDS_PER_INCH on SnapHelper as you want and then use simply use the LinearSnapHelper you created