How to make RecyclerView scroll smoothly?
Asked Answered
D

10

36

This is more like a generic question, but after lot of search and try I am not able to understand why this is so difficult to achieve. This is the closest answer I can find but still unable to implement.

To be specific I am using RecyclerView with GridLayoutManager. All I want is the grid layout to scroll smoothly (like default gallary app) ,nothing fancy, but the default implementation of grid layout manager scrolls the view in a 'jerky' manner. I tried to implement the method from above link but unsuccessfully.

I also tried to implement LinearSmoothScroller but I am not sure how to implement computeScrollVectorForPosition method. Google's documentation on computeScrollVectorForPosition literally has 0 words.

I found this 3 part tutorial, but it was of very little help. So, all I want to ask is: can there be some kind of template code which we can implement in LinearSmoothScroller or by extending RecyclerView.SmoothScroller and achieve smooth scrolling ? Even if the code depends on number of items and items per row in gridlayout, there has to be some method to do it easily. Am I missing something here ?

Duumvirate answered 6/7, 2015 at 15:4 Comment(4)
"Am I missing something here ?" -- perhaps you should determine why your existing code is "jerky", using tools like Traceview, StrictMode, and so on.Boulevardier
@commonsWare So you mean default implementation of GridLayoutManager should scroll the gridview smoothly ? I am sorry if I sound noob, I am quite new to Android Programming.Duumvirate
"So you mean default implementation of GridLayoutManager should scroll the gridview smoothly ?" -- yes, at least for my definition of "smoothly". The typical source of "jerky" scrolling in Android is the app taking too much time on the main application thread updating the UI. In the case of RecyclerView, this would mean taking too much time in onBindViewHolder() or possibly in onCreateViewHolder(). Those each need to return in sub-millisecond times, meaning you cannot do disk I/O or network I/O in them.Boulevardier
@Boulevardier Thank you for your answer, I think I found the problem. I am using holder.photo.setImageBitmap(BitmapFactory.decodeFile(itemList.get(position).getAbsolutePath())); on onBindViewHolder() and the image file is around 100kb each with about a dozen images in the list. I will try to do a better implementation of it.Duumvirate
B
43

The typical source of "jerky" scrolling in Android is the app taking too much time on the main application thread updating the UI. In the case of RecyclerView, this would mean taking too much time in onBindViewHolder() or possibly in onCreateViewHolder(). Those each need to return in sub-millisecond times, meaning you cannot do disk I/O or network I/O in them.

think I found the problem. I am using holder.photo.setImageBitmap(BitmapFactory.decodeFile(itemList.get(position).get‌​AbsolutePath())); on onBindViewHolder() and the image file is around 100kb each with about a dozen images in the list

Yes, that will be doing disk I/O and image decoding on the main application thread. That will be slow enough to cause jank in the UI (i.e., "jerky" scrolling).

Consider using an image loading library, like Picasso or Universal Image Loader, as they can populate your ImageView from the bitmap asynchronously.

This sample app uses Universal Image Loader to help populate the contents of a RecyclerView (with GridLayoutManager), with the data source being the available videos on the device.

Boulevardier answered 6/7, 2015 at 15:27 Comment(6)
Thank you, I will check them and implement. In the mean time how about using this simple solution rather than using a seperate library ?Duumvirate
@rockfight: Because that has little to do with your problem. The focus of that page is memory efficiency, not main application thread time.Boulevardier
UIL does not work while image resides in sd card (path of file starts with /storage. I used Glide for this purpose, and it provided a perfect solution to my problemCreationism
This is work for me. I face same issue with image large size. When we set image in ImageView using Bitmap then it work charms as @Boulevardier answer.Preprandial
@CommonsWare, in my app the recyclerview scrolling is not smooth, since i am inflating views inside onBindViewHolder, is there any alternative way to add a view without layout inflater.Honesty
@SumighoshCharuvil: Inflation is unlikely to be the problem; it would take you just about as much time to create the views directly via Java constructors and setters. Use method tracing in your IDE to determine exactly where your problem lies.Boulevardier
B
49

Add this wherever you have declared RecyclerView in your Activity or Fragment

RecyclerView mRecyclerview = (RecyclerView) findViewById(...);
mRecyclerview.setNestedScrollingEnabled(false);

setNestedScrollview(false) does the work for you.

Blithe answered 15/4, 2017 at 4:6 Comment(0)
B
43

The typical source of "jerky" scrolling in Android is the app taking too much time on the main application thread updating the UI. In the case of RecyclerView, this would mean taking too much time in onBindViewHolder() or possibly in onCreateViewHolder(). Those each need to return in sub-millisecond times, meaning you cannot do disk I/O or network I/O in them.

think I found the problem. I am using holder.photo.setImageBitmap(BitmapFactory.decodeFile(itemList.get(position).get‌​AbsolutePath())); on onBindViewHolder() and the image file is around 100kb each with about a dozen images in the list

Yes, that will be doing disk I/O and image decoding on the main application thread. That will be slow enough to cause jank in the UI (i.e., "jerky" scrolling).

Consider using an image loading library, like Picasso or Universal Image Loader, as they can populate your ImageView from the bitmap asynchronously.

This sample app uses Universal Image Loader to help populate the contents of a RecyclerView (with GridLayoutManager), with the data source being the available videos on the device.

Boulevardier answered 6/7, 2015 at 15:27 Comment(6)
Thank you, I will check them and implement. In the mean time how about using this simple solution rather than using a seperate library ?Duumvirate
@rockfight: Because that has little to do with your problem. The focus of that page is memory efficiency, not main application thread time.Boulevardier
UIL does not work while image resides in sd card (path of file starts with /storage. I used Glide for this purpose, and it provided a perfect solution to my problemCreationism
This is work for me. I face same issue with image large size. When we set image in ImageView using Bitmap then it work charms as @Boulevardier answer.Preprandial
@CommonsWare, in my app the recyclerview scrolling is not smooth, since i am inflating views inside onBindViewHolder, is there any alternative way to add a view without layout inflater.Honesty
@SumighoshCharuvil: Inflation is unlikely to be the problem; it would take you just about as much time to create the views directly via Java constructors and setters. Use method tracing in your IDE to determine exactly where your problem lies.Boulevardier
E
17

I just faced this problem, and disabling nested scroll fixed it. Do it like this:

yourRecyclerview.setNestedScrollingEnabled(false);

Or you can change the value in xml file where you defined RecyclerView:

<android.support.v7.widget.RecyclerView
    android:id="@+id/yourRecyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:nestedScrollingEnabled="false"/>
Extemporaneous answered 13/2, 2018 at 10:46 Comment(1)
Thanks @Arman i use RecyclerView within NestedScrollView after that i got this. But now it's working fine.Paleoasiatic
S
6

I just faced this problem, and i got I put the RecyclerView in ScrollView So please never put the RecyclerView inside the ScrollView that may also cause.

mLayoutManager.findFirstVisibleItemPosition()

That always return a fixed value. And also check

recyclerview.setNestedScrollingEnabled(false);
Shipmate answered 1/9, 2018 at 10:11 Comment(0)
R
5

I made the scrolling smooth by overriding the calculateSpeedPerPixel(DisplayMetrics displayMetrics) method:

public class CustomLayoutManager extends LinearLayoutManager {

    protected CenterLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class CenterSmoothScroller extends LinearSmoothScroller {

        CenterSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return 0.5f; //pass as per your requirement
        }
    }
}
Rourke answered 8/5, 2018 at 15:27 Comment(1)
overwrite calculateSpeedPerPixel work for me, the default will make recycler view like jumping when calling smoothScrollToPosition(position), but when overwrite this method and return 0.5f works perfectlyNguyetni
K
1
android:animateLayoutChanges="false"
Kippie answered 19/10, 2016 at 15:24 Comment(1)
so which is it?Gav
B
0

You need to use the "Picasso" library to load image asynchronously, and also make sure that the size of the image which you load is similar to the size of image view in the layout, using the fit() function. Also, if you are using a recycler view inside the scroll view, make sure to set nested scrolling to false.

I solved my issue using the above three methods

Balanchine answered 4/2, 2020 at 12:53 Comment(0)
J
0

Use a library as said by other comments in here. Also make sure you do not have Recyclerviews nested within a Scrollview. This was one of the reason why mine previously wasn't so smooth.

Jackquelin answered 24/5, 2020 at 1:31 Comment(0)
C
0

After 3 days spend in it, I solved the smooth scroll issue in my project.

The problem is <layer-list> drawable set in the background of item_user.xml file so it takes GPU time for rendering that's why scrolling not smooth.

Don't use <layer-list> or any other complex drawable in adapter item.

Crescent answered 9/7, 2021 at 10:47 Comment(0)
E
-4

just add this

animateLayoutChanges = true 

in recyclerview xml

Evacuate answered 22/8, 2016 at 13:13 Comment(1)
so which is it?Gav

© 2022 - 2024 — McMap. All rights reserved.