Android : Control Smooth scroll over recycler view
Asked Answered
O

4

54

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

Odom answered 2/3, 2015 at 5:40 Comment(10)
@XaverKapeller I want to implement Slow scrolling in RecyclerView.Odom
What do you mean with slow scrolling? Please explain more. Do you want to programmatically scroll to some other position?Encephalogram
Still don't understand. What scrolling speed? What are you talking about? The scrolling speed when you programmatically scroll to another position?Encephalogram
My RecyclerView is endless with Items (Near about 10000 Item). When user scrolls RecyclerView Its scrolling like a wind unable to see Items. So my idea is to slow down speed of scrolling. I hope Now you get What I am trying to say.Odom
So you mean when the user flings the RecyclerView or what?Encephalogram
@XaverKapeller no I mean scrolling a screen.Odom
I have no idea what you are talking about. If the user scrolls like normal with his finger on the screen then the data moves with the finger. I don't understand how you would ever get a situation in which ass you say "Its scrolling like a wind unable to see Items".Encephalogram
It happens when I move finger fast. and yes you got the point that scrolling with finger.Odom
@XaverKapeller Sorry, Actually I don't know that I actually want a fling.Odom
I you are after a solution for controlling the speed of the scroll, to scroll faster to more distant positions have a look at https://mcmap.net/q/339603/-android-recyclerview-fake-smoothscroll-to-top-if-many-many-itemsUvea
I
112

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.

Intenerate answered 4/3, 2015 at 11:27 Comment(15)
What I want is to scroll down manual scrolling your second point. But methods and variables there are undefined. can you please update this.Odom
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.Intenerate
If instead of creating your own layout manager, your give the RecyclerView a new LinearLayoutManager, does the scrolling issue stop?Intenerate
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.Intenerate
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.Odom
This looks pretty good, but what's the deal with getScrollState()? That doesn't do anything for me in the LayoutManager class.Hitandrun
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.Intenerate
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.Hitandrun
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.Intenerate
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(targetPosition);Winnie
@GilMoshayof any idea how can I take offset into account aswell ? if i intend to do scrollToPositionWithOffset(pos,offset) instead of smoothScrollToPosition() ?Opportuna
and calculateCurrentDistanceToPosition() ? where do you get this from?Marlea
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.Intenerate
I read this answer and it work for me. #31249752Reefer
Hey @GilMoshayof can you please help me with this #49953465Cyrano
O
36

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);
Odom answered 10/6, 2015 at 5:27 Comment(9)
it does nothing for meMatador
What you did ? as per I described in my answerOdom
I did the same. But somehow setting velocityY to different values (even 0.1) does nothingMatador
did you change in xml and java?Odom
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 +1Matador
@Matador 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.Odom
This does the job for me. ThanksOchlophobia
This doesn't work for smoothScrollToPosition. it's only for user interactionDanidania
@Danidania above code is for fling only. For smoothScrollToPosition use this answer https://mcmap.net/q/334668/-android-control-smooth-scroll-over-recycler-viewOdom
V
36

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);
    }

};
Viscacha answered 24/8, 2016 at 21:45 Comment(5)
This helped me a lot. Thank you for posting!Cobbett
you save a lot of time.Kinesthesia
How do I make it smooth scroll to a position in 1 second, for example?Masturbate
i get an error like "java.lang.IllegalStateException: If you provide an interpolator, you must set a positive duration". It needs duration.Blasphemy
@Blunderer, You're geniusSatinet
F
0

use this for smooth scrolling

recyclerViewCart.isNestedScrollingEnabled = false
Formality answered 29/12, 2020 at 6:24 Comment(1)
I recommend that you add comments to your code as it will help other members to understand what you intended with this code.Primula

© 2022 - 2024 — McMap. All rights reserved.