Android : Control Smooth scroll over recycler view
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);
}
};
![Shabbir Dhangot](https://i.stack.imgur.com/vjfG0.jpg?s=256&g=1)
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, 2022Comments
-
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 over 9 yearsWhat 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 over 9 yearsI'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 over 9 yearsIf instead of creating your own layout manager, your give the RecyclerView a new LinearLayoutManager, does the scrolling issue stop?
-
Gil Moshayof over 9 yearsYou 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 over 9 yearsOkk 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 over 9 yearsThis looks pretty good, but what's the deal with
getScrollState()
? That doesn't do anything for me in the LayoutManager class. -
Gil Moshayof over 9 yearsThe 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 over 9 yearsI 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 over 9 yearsOf 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 over 9 yearsAhhh that makes sense. Thanks!
-
Bartek Lipinski about 9 yearsWow, the first one (overriding
calculateSpeedPerPixel
) is pretty clever. Good job! Although overridingcomputeScrollVectorForPosition
can be achieved with just a simple<your_layout_manager>.this.computeScrollVectorForPosition(targetPosition);
-
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 over 8 yearsand calculateCurrentDistanceToPosition() ? where do you get this from?
-
Gil Moshayof over 8 yearsThis 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 over 8 yearsit does nothing for me
-
Shabbir Dhangot over 8 yearsWhat you did ? as per I described in my answer
-
Androider over 8 yearsI did the same. But somehow setting velocityY to different values (even 0.1) does nothing
-
Shabbir Dhangot over 8 yearsdid you change in xml and java?
-
Androider over 8 yearsExcuse 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 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 over 7 yearsThis helped me a lot. Thank you for posting!
-
Vik over 7 yearsThis does the job for me. Thanks
-
Ninja about 7 yearsI read this answer and it work for me. stackoverflow.com/questions/31249252/…
-
abbath0767 almost 7 yearsyou save a lot of time.
-
android developer over 6 yearsHow do I make it smooth scroll to a position in 1 second, for example?
-
Admin about 6 yearsHey @GilMoshayof can you please help me with this stackoverflow.com/questions/49952965/…
-
Umut ADALI over 5 yearsi get an error like "java.lang.IllegalStateException: If you provide an interpolator, you must set a positive duration". It needs duration.
-
Shayan_Aryan almost 5 yearsThis doesn't work for
smoothScrollToPosition
. it's only for user interaction -
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 over 3 years@Blunderer, You're genius
-
Joe Ferndz over 3 yearsI recommend that you add comments to your code as it will help other members to understand what you intended with this code.