LayoutManager / LinearLayoutManager / LinearSmoothScroller / RecyclerView / SmoothScroller

Scrolling RecyclerView – Part 2

In the previous article we got a basic RecyclerView with a large data set working and found that the smoothScrollToPosition() behaviour worked well when moving relatively small amounts, but was far too slow to be usable when moving much larger distances. In this article we’ll explore why this is by taking a deep dive in to the LinearLayoutManager and SmoothScroller components of RecyclerView.

The problem that we’re trying to solve is rather more complex than it first appears. The obvious model that springs to mind for this is that of a Property Animation where we set a start and end value and then set a duration. When this animation is run it performs the required transition within the specified duration. However this is clearly not happening because a smoothScrollToPosition() over a short range occurs much faster than a smoothScrollToPosition() over a much larger range, as we have already seen:

For RecyclerView the complexity lies in determining the end value. Much of the efficiency of RecyclerView is born out of the fact that the only individual items that it holds the Views for are the ones currently being displayed, and as we scroll though the list the items going out of the viewport being recycled and re-bound to the data of the items coming in to the viewport. So at any one time the RecyclerView only knows the layout dimension of the individual list items currently within the viewport. The problem is that if we are requesting a smooth scroll to a position in the list far removed from the current position, the RecyclerView (or more specifically the LayoutManager, which is responsible for the scrolling) does not know the precise distance in pixels that we need to scroll through in order to reach the desired position because the list items will not necessarily be of fixed dimension, particularly for lists with multiple view types and views which may change size dependent on the data to which they are being bound.

If we take a look at the source for LinearLayoutManager we can see that a method named smoothScrollToPosition() which will be called by the corresponding method in RecyclerView:

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
        int position) {
    LinearSmoothScroller linearSmoothScroller =
            new LinearSmoothScroller(recyclerView.getContext()) {
                @Override
                public PointF computeScrollVectorForPosition(int targetPosition) {
                    return LinearLayoutManager.this
                            .computeScrollVectorForPosition(targetPosition);
                }
            };
    linearSmoothScroller.setTargetPosition(position);
    startSmoothScroll(linearSmoothScroller);
}

This creates an instance of LinearSmoothScroller which is responsible for performing the actual scroll. This isn’t a trivial class – it contains an awful lot of logic. However the behaviour that it implements is fairly simple to understand. Rather than try and determine the exact number of pixels that it needs to scroll through before reaching the end point, it performs a series of 10000 pixel flings at a speed of 25 milliseconds per inch until the end target item comes in to the viewport, and then it decelerates to stop with the end target visible. (I appreciate that speed is the measure of distance / time so inches per millisecond would be more accurate, but milliseconds per inch is used within the LinearSmoothScroller code so I’m using that to keep the explanation understandable to those that are looking at the source)

It is therefore impossible to set a fixed duration for this entire sequence as the number of 10000 pixel flings required to bring the target list item in to the viewport simply cannot be determined ahead of time.

So now that we have an understanding of how LinearSmoothScroller actually works it is easy to understand why it is simply not possible to set a fixed duration for the scroll because it simply doesn’t know how the scroll distance in pixels until it reaches the destination.

In the next article we’ll begin to look at ways of changing the smooth scrolling behaviour.

As this article has been explanatory we’ve not actually modified any code so there is no new code to look at although the source for the previous article is available here.

© 2015, Mark Allison. All rights reserved.

Copyright © 2015 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.