Android : Control Smooth scroll over recycler view

70,854

Solution 1

It's unclear what you mean when you say "smoothScroll". You could be referring to the automatic "smoothScrollToPosition" which will automatically scroll to a specified position, you could be talking about manual scrolling and you could be talking about flinging. For the sake of prosperity, I will attempt to answer all of these issues now.

1. Automatic smooth scrolling.

Inside your layout manager, you need to implement the smoothScrollToPosition method:

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position)
    {
        // A good idea would be to create this instance in some initialization method, and just set the target position in this method.
        LinearSmoothScroller smoothScroller = new LinearSmoothScroller(getContext())
        {
            @Override
            public PointF computeScrollVectorForPosition(int targetPosition)
            {
                int yDelta = calculateCurrentDistanceToPosition(targetPosition);
                return new PointF(0, yDelta);
            }

            // This is the important method. This code will return the amount of time it takes to scroll 1 pixel.
            // This code will request X milliseconds for every Y DP units.
            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics)
            {
                return X / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, Y, displayMetrics);
            }

        };
        smoothScroller.setTargetPosition(position);

        startSmoothScroll(smoothScroller);
    }

In this example, I use a helper method named "calculateCurrentDistanceToPosition". This can be a bit tricky, since it involves keeping track of your current scroll position, and calculating the scroll position of a given y position. You can find an example of how to keep track of the recycler's scroll y here.

Calculating the scroll y of a given position is really dependent on what your recycler is displaying. Assuming all your items are the same height, you can calculate this by performing the following calculation:

targetScrollY = targetPosition * itemHeight

Then, to calculate the distance you need to scroll, simply subtract the current scroll y with the target scroll y:

private int calculateCurrentDistanceToPosition(int targetPosition) {
    int targetScrollY = targetPosition * itemHeight;
    return targetScrollY - currentScrollY;
}

2. Slowing down manual scrolling.

Once again, you need to edit your layout manager, this time - the scrollVerticallyBy method:

    @Override
    public int scrollVerticallyBy(int delta, Recycler recycler, State state)
    {
       // write your limiting logic here to prevent the delta from exceeding the limits of your list.

       int prevDelta = delta;
       if (getScrollState() == SCROLL_STATE_DRAGGING)
            delta = (int)(delta > 0 ? Math.max(delta * MANUAL_SCROLL_SLOW_RATIO, 1) : Math.min(delta * MANUAL_SCROLL_SLOW_RATIO, -1));  

       // MANUAL_SCROLL_SLOW_RATIO is between 0 (no manual scrolling) to 1 (normal speed) or more (faster speed).
       // write your scrolling logic code here whereby you move each view by the given delta

        if (getScrollState() == SCROLL_STATE_DRAGGING)
            delta = prevDelta;

        return delta;
    }

Edit: In the above method, I call "getScrollState()". This is a method of RecyclerView. In this implementation, my custom LayoutManager is a nested class of my custom RecyclerView. If this doesn't work for you, you can try to grab the scroll state via some interface pattern.

3. Slow down the fling speed

Here you want to scale down the fling velocity. You will need to override the fling method inside your RecyclerView subclass:

@Override
public boolean fling(int velocityX, int velocityY)
{
     velocityY *= FLING_SCALE_DOWN_FACTOR; // (between 0 for no fling, and 1 for normal fling, or more for faster fling).

     return super.fling(velocityX, velocityY);
}

It's difficult for me to provide a more tailored solution, since you didn't post any of your code, or provide much information about your setup, but I hope this covers most bases and will help you find the best solution for you.

Solution 2

I just simplifying Answer how to use it to control smooth scroll.

Create Class

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;

public class CustomRecyclerView extends RecyclerView {

    Context context;

    public CustomRecyclerView(Context context) {
        super(context);
        this.context = context;
    }

    public CustomRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean fling(int velocityX, int velocityY) {

        velocityY *= 0.7;
        // velocityX *= 0.7; for Horizontal recycler view. comment velocityY line not require for Horizontal Mode.

        return super.fling(velocityX, velocityY);
    }

}

Adjust speed by Replacing 0.7 to your value.

Now use this class in your XML like this.

<<yourpackage>.CustomRecyclerView
    android:id="@+id/my_recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical" />

Read RecyclerView in Java like.

CustomRecyclerView mRecyclerView;
mRecyclerView = (CustomRecyclerView) findViewById(R.id.my_recycler_view);

Solution 3

Simply implement smoothScrollToPosition() of your LinearLayoutManager:

LinearLayoutManager layoutManager = new LinearLayoutManager(this) {

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        LinearSmoothScroller smoothScroller = new LinearSmoothScroller(this) {

            private static final float SPEED = 300f;// Change this value (default=25f)

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                return SPEED / displayMetrics.densityDpi;
            }

        };
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

};
Share:
70,854
Shabbir Dhangot
Author by

Shabbir Dhangot

I am Android and Angular Developer from India. Find out My Blog related to android. It will be helpful to you. Follow me on twitter and DM me for any problem you facing related to Angular, Javascript, Android, total.js

Updated on July 14, 2022

Comments

  • Shabbir Dhangot
    Shabbir Dhangot almost 2 years

    I am using Recyclerview with CardView. I am aware how to control speed on list view. But not for Recyclerview.

    I searched a lot in found class name SmoothScroll. How to use that? I have no Idea! Right now Recyclerview by default scroll is fast.

    UPDATE:

    I Summarized Gil Answer with this

  • Shabbir Dhangot
    Shabbir Dhangot over 9 years
    What I want is to scroll down manual scrolling your second point. But methods and variables there are undefined. can you please update this.
  • Gil Moshayof
    Gil Moshayof over 9 years
    I'm afraid that in order to help you any further, I'll need you to post more of your code so I can understand what you're doing and where you're stuck.
  • Gil Moshayof
    Gil Moshayof over 9 years
    If instead of creating your own layout manager, your give the RecyclerView a new LinearLayoutManager, does the scrolling issue stop?
  • Gil Moshayof
    Gil Moshayof over 9 years
    You copied my code sample, but didn't adapt it to your needs... "dy" needs to be refactored to "detla", MANUAL_SCROLL_SLOW_RATIO is a final static variable which you must define, and should be between 0 and 1, and of course, you need to implement your own logic for adjusting the positions of all the views, as well as detatching / scrapping unneeded views.
  • Shabbir Dhangot
    Shabbir Dhangot over 9 years
    Okk I understand. But using with new LinearLayoutManager facing same issue. I cant understand my mistake. I follow your guideline and create new class. its not giving error now. but I unable to see recycler view at runtime. Is anything I need to put in getDefaultLayoutParams() or any other method.
  • Wenger
    Wenger over 9 years
    This looks pretty good, but what's the deal with getScrollState()? That doesn't do anything for me in the LayoutManager class.
  • Gil Moshayof
    Gil Moshayof over 9 years
    The idea here is to ensure that the user is manually dragging. scrollVerticallyBy gets called by automatic smooth scrolls and flings. If you want to slow down (or speed up) manual scrolling (i.e., items will follow your finger at different speeds than your finger), then you should use this method.
  • Wenger
    Wenger over 9 years
    I got all that, but there isn't a getScrollState() method available in that class unless it points to a custom one that retrieves scroll state through some other means.
  • Gil Moshayof
    Gil Moshayof over 9 years
    Of course! I should have mentioned that in this implementation, my layout manager is a nested class of my recyclerview. Ill edit the answer shortly. Thank you.
  • Wenger
    Wenger over 9 years
    Ahhh that makes sense. Thanks!
  • Bartek Lipinski
    Bartek Lipinski about 9 years
    Wow, the first one (overriding calculateSpeedPerPixel) is pretty clever. Good job! Although overriding computeScrollVectorForPosition can be achieved with just a simple <your_layout_manager>.this.computeScrollVectorForPosition(ta‌​rgetPosition);
  • ShahrozKhan91
    ShahrozKhan91 about 9 years
    @GilMoshayof any idea how can I take offset into account aswell ? if i intend to do scrollToPositionWithOffset(pos,offset) instead of smoothScrollToPosition() ?
  • rubmz
    rubmz over 8 years
    and calculateCurrentDistanceToPosition() ? where do you get this from?
  • Gil Moshayof
    Gil Moshayof over 8 years
    This method is up to you to implement but can be fairly simple if you keep track of how far you've scrolled in your layout manager, and if you have a way of knowing the scroll position of a certain element. Then you basically return targetScrollOffset - currentScrollOffset. If you'd like to know more, perhaps consider opening a new SO question and link it here so I can answer it.
  • Androider
    Androider over 8 years
    it does nothing for me
  • Shabbir Dhangot
    Shabbir Dhangot over 8 years
    What you did ? as per I described in my answer
  • Androider
    Androider over 8 years
    I did the same. But somehow setting velocityY to different values (even 0.1) does nothing
  • Shabbir Dhangot
    Shabbir Dhangot over 8 years
    did you change in xml and java?
  • Androider
    Androider over 8 years
    Excuse me, I am using horizontal mode and I just needed to use velocityX instead of velocityY. Now everything works excellent. Thank your for feedback +1
  • Shabbir Dhangot
    Shabbir Dhangot over 8 years
    @Androider Thank you for information. I will edit my answer for who uses horizontal mode. If you think anything need edit in answer feel free to edit.
  • Marty Miller
    Marty Miller over 7 years
    This helped me a lot. Thank you for posting!
  • Vik
    Vik over 7 years
    This does the job for me. Thanks
  • Ninja
    Ninja about 7 years
    I read this answer and it work for me. stackoverflow.com/questions/31249252/…
  • abbath0767
    abbath0767 almost 7 years
    you save a lot of time.
  • android developer
    android developer over 6 years
    How do I make it smooth scroll to a position in 1 second, for example?
  • Admin
    Admin about 6 years
    Hey @GilMoshayof can you please help me with this stackoverflow.com/questions/49952965/…
  • Umut ADALI
    Umut ADALI over 5 years
    i get an error like "java.lang.IllegalStateException: If you provide an interpolator, you must set a positive duration". It needs duration.
  • Shayan_Aryan
    Shayan_Aryan almost 5 years
    This doesn't work for smoothScrollToPosition. it's only for user interaction
  • Shabbir Dhangot
    Shabbir Dhangot almost 5 years
    @Shayan_Aryan above code is for fling only. For smoothScrollToPosition use this answer stackoverflow.com/a/39133502/2900893
  • Gary Chen
    Gary Chen over 3 years
    @Blunderer, You're genius
  • Joe Ferndz
    Joe Ferndz over 3 years
    I recommend that you add comments to your code as it will help other members to understand what you intended with this code.