How can I control the scrolling speed of recyclerView.smoothScrollToPosition(position)?

I have a recycler view, and I want a smooth scrolldown and then scrollup to it programatically to show the complete content in it to user.

I can do this by:

    final int height=recyclerView.getChildAt(0).getHeight();
    recyclerView.smoothScrollToPosition(height);
    recyclerView.postDelayed(new Runnable() {
        public void run() {
            recyclerView.smoothScrollToPosition(0);
                        }
    },200);

But what I want is to slow down the scrolling speed, so that the complete content gets visible clearly.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Just to improve on the answer a little:

public class SpeedyLinearLayoutManager extends LinearLayoutManager {

    private static final float MILLISECONDS_PER_INCH = 5f; //default is 25f (bigger = slower)

    public SpeedyLinearLayoutManager(Context context) {
        super(context);
    }

    public SpeedyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public SpeedyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {

        final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {

            @Override
            public PointF computeScrollVectorForPosition(int targetPosition) {
                return super.computeScrollVectorForPosition(targetPosition);
            }

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

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

And then set SpeedyLayoutManager to your RecyclerView:

recyclerView.setLayoutManager(new SpeedyLinearLayoutManager(context, SpeedyLinearLayoutManager.VERTICAL, false);

Solution 2

For those uninterested in overriding LinearLayoutManager (or whichever subclass you’re using), you can just call startSmoothScroll(smoothScroller) directly on the layout manager.

LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {

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

linearSmoothScroller.setTargetPosition(position);
layoutManager.startSmoothScroll(linearSmoothScroller);

Solution 3

I found an answer!

You need to make a custom class extending [LinearLayoutManager][1] and then override the [smoothScrollToPosition][1] method. Inside, you need to create a new [LinearSmoothScroller][1] and override its calculateSpeedPerPixel method Then use that LinearSmoothScroller to complete the scroll.

Here is my example :

public class CustomLinearLayoutManager extends LinearLayoutManager{
        @Override
        public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
            final LinearSmoothScroller linearSmoothScroller =
                    new LinearSmoothScroller(recyclerView.getContext()) {
                        private static final float MILLISECONDS_PER_INCH = 100f;

                        @Override
                        public PointF computeScrollVectorForPosition(int targetPosition) {
                            return CustomLinearLayoutManager.this
                                .computeScrollVectorForPosition(targetPosition);
                    }

                    @Override
                    protected float calculateSpeedPerPixel
                            (DisplayMetrics displayMetrics) {
                        return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
                    }
                };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }
}

Assign this CustomLinearLayoutManager to your RecyclerView and you should be good to go. Let me know if I was unclear in anything. I’ll be happy to help.

Solution 4

I’ve made a version that you can set the scrolling speed variable.

If you set factor to 2, it will be twice as slow as default.

public class VariableScrollSpeedLinearLayoutManager extends LinearLayoutManager {

    private final float factor;

    public VariableScrollSpeedLinearLayoutManager(Context context, float factor) {
        super(context);
        this.factor = factor;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {

        final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {

            @Override
            public PointF computeScrollVectorForPosition(int targetPosition) {
                return VariableScrollSpeedLinearLayoutManager.this.computeScrollVectorForPosition(targetPosition);
            }

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                return super.calculateSpeedPerPixel(displayMetrics) * factor;
            }
        };

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

Solution 5

Based on @Bartek Mrozinski’s answer. This is my Kotlin extension function for smoothly scroll the RecyclerView in a given time .

fun RecyclerView.smoothSnapToPosition(position: Int, snapMode: Int = LinearSmoothScroller.SNAP_TO_START) {
val scrollDuration = 500f;
val smoothScroller = object : LinearSmoothScroller(this.context) {
    override fun getVerticalSnapPreference(): Int = snapMode
    override fun getHorizontalSnapPreference(): Int = snapMode
    override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float {
        return scrollDuration / computeVerticalScrollRange();
    }
}
smoothScroller.targetPosition = position
layoutManager?.startSmoothScroll(smoothScroller) 
}

Solution 6

To smoothly scroll the RecyclerView in a given time You can override the LinearSmoothScroller#calculateSpeedPerPixel like in example below.

final Context context = getContext();
final RecyclerView recyclerView = new RecyclerView(context);

final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);

final YourCustomAdapter adapter = new YourCustomAdapter();
recyclerView.setAdapter(new YourCustomAdapter());

final float scrollDuration = 30000f; // in milliseconds
final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
    @Override
    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
        return scrollDuration / recyclerView.computeVerticalScrollRange();
    }
};

linearSmoothScroller.setTargetPosition(adapter.getItemCount() - 1);
layoutManager.startSmoothScroll(linearSmoothScroller);

Solution 7

//i am using the handler..
 //where cretedactivtyadapter is my adpater classs
// instiate your adpter class like this
 private CreatedActivityAdapter createdActivityAdapter;

//call autoscroll in your oncreate//
public void autoscroll()
{ ////auto scroll is used for the recycler view to scroll it self
    final int speedScroll = 1;
    final Handler handler = new Handler();
    final Runnable runnable = new Runnable() {
        int count = 0;
        @Override
        public void run()
        {
            if (count == createdActivityAdapter.getItemCount())
                count=0;
            if (count < createdActivityAdapter.getItemCount()){
                recyclerView.smoothScrollToPosition(++count);
                handler.postDelayed(this,speedScroll);
            }
        }
    };
    handler.postDelayed(runnable,speedScroll);
}

//and call theis handler in your on create

Solution 8

How can I control the scrolling speed of recyclerView.smoothScrollToPosition(position)?

I got it like this horizontally

  private val milliSecondsPerInch = 500f 
  var horizontalLayoutManager =  LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)

onCreate

horizontalLayoutManager =  LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recycler_banner?.layoutManager = horizontalLayoutManager

and then

val linearSmoothScroller: LinearSmoothScroller =
object : LinearSmoothScroller(this) {
    override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
        return milliSecondsPerInch / displayMetrics.densityDpi
    }
}
linearSmoothScroller.targetPosition = 3 //the last position of the item in the list
horizontalLayoutManager.startSmoothScroll(linearSmoothScroller)

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply