Building a RecyclerView LayoutManager – Part 1

This article is Part 1 in our series. Here are links to Part 2 and Part 3 as well.

By now, if you’re an Android developer paying any attention, you’ve at least heard of RecyclerView; a new component that will be added to the support library to facilitate custom implementations of high-performance view collections by facilitating view recycling. Others have already done a remarkable job describing the basics of how to use RecyclerView with the built-in pieces already provided, including item animations. So rather than dive into all that again, here are some resources to get you up to speed:

In this series of posts, we will be focused on the low-level details involved in building your own LayoutManager implementation, to do something a bit more complex than a simple vertical or horizontal scrolling list.

Before going any further, a warning is in order. The LayoutManager API allows powerful and complex layout recycling because it doesn’t do much for you; these implementations involve a fair amount of code you have to write yourself. As with any project involving custom views, don’t get caught in a trap of over-optimizing or over-generalizing your code. Build the features you need for the application use case you’re concerned with.

RecyclerView Playground

All the code snippets in this post series are taken from the RecyclerView Playground sample that I have posted on GitHub. This sample application includes examples of various aspects of working with RecyclerView, from creating simple lists, to custom LayoutManagers.

Code for this post is drawn from the FixedGridLayoutManager example; a two-dimensional grid layout with full scrolling in both the horizontal and vertical directions.

Support Library Samples

The support library also has a sample that includes a custom LayoutManager; it is essentially a custom implementation of a vertical linear list: [SDK_PATH]/extras/android/compatibility/samples/Support7Demos/src/com/example/android/supportv7/widget/

Also, while much of Android “L” and the new support libraries may not yet be in AOSP, the RecyclerView support artifact ships with a sources JAR you can view inside your environment: [SDK_PATH]/extras/android/m2repository/com/android/support/recyclerview-v7/21.0.0-rc1/recyclerview-v7-21.0.0-rc1-sources.jar

The Recycler

First, a bit of insight into how the API is structured. Your LayoutManager is given access to a Recycler instance at key points in the process when you might need to recycle old views, or obtain new views from a potentially recycled previous child.

The Recycler also removes the need to directly access the view’s current adapter implementation. When your LayoutManager requires a new child view, simply call getViewForPosition() and the Recycler will return the view with the appropriate data already bound. The Recycler takes care of determining whether a new view must be created, or if an existing scrapped view gets reused. Your responsibility, inside your LayoutManager, is to ensure that views which are no longer visible get passed to the Recycler in a timely manner; this will keep the Recycler from creating more view objects than is necessary.

Detach vs. Remove

There are two ways to handle existing child views during a layout update: detach and remove. Detach is meant to be a lightweight operation for reordering views. Views that are detached are expected to be re-attached before your code returns. This can be used to modify the indices of attached child views without re-binding or re-creating those views through the Recycler.

Remove is meant for views that are no longer needed. Any view that is permanently removed should be placed in the Recycler for later re-use, but the API does not enforce this. It is up to you whether the views you remove also get recycled.

Scrap vs. Recycle

Recycler has a two-level view caching system: the scrap heap and the recycle pool. The scrap heap represents a lighter weight collection where views can be returned to the LayoutManager directly without passing through the adapter again. Views are typically placed here when they are temporarily being detached, but will be re-used within the same layout pass. The recycle pool consists of views that are assumed to have incorrect data (data from a different position), so they will always be passed through the adapter to have data re-bound before they are returned to the LayoutManager.

When attempting to supply the LayoutManager with a new view, a Recycler will first check the scrap heap for a matching position/id; if one exists, it will be returned without re-binding to the adapter data. If no matching view is found, the Recycler will instead pull a suitable view from the recycle pool and bind the necessary data to it from the adapter (i.e. RecyclerView.Adapter.bindViewHolder() is invoked) before returning it. In cases where no valid views exist in the recycle pool, a new view will be created instead (i.e. RecyclerView.Adapter.createViewHolder() is invoked) before being bound, and returned.

Rule of Thumb

The LayoutManager API lets you do pretty much all of these tasks independently if you wish, so the possible combinations can be a bit numerous. In general, use detachAndScrapView() for views you want to temporarily reorganize and expect to re-attach inside the same layout pass. Use removeAndRecycleView() for views you no longer need based on the current layout.

Building The Core

A LayoutManager implementation is responsible for attaching, measuring, and laying out all the child views it needs in real-time. As the user scrolls the view, it will be up to the layout manager to determine when new child views need to be added, and old views can be detached and scrapped.

You will need to override and implement the following method to create a minimum viable LayoutManager.


This is actually the only required override to get your LayoutManager to compile. The implementation is pretty straightforward, just return a new instance of the RecyclerView.LayoutParams that you want applied by default to all the child views returned from the Recycler. These parameters will be applied to each child before they return from getViewForPosition().

public RecyclerView.LayoutParams generateDefaultLayoutParams() {
    return new RecyclerView.LayoutParams(


This is the main entry point into your LayoutManager. This method will be called when the view needs to do an initial layout, and again whenever the adapter data set changes (or the entire adapter is swapped out). This method is NOT called for every possible change you need to make to the layout. It is a good opportunity to reset the layout of child views for either an initial pass or a data set change.

In the next segment, we will look at how this can be used to do a fresh layout based on the currently visible elements when the adapter updates. For now, we’ll address it simply as the first pass for laying out child views. Below is a simplified version of what exists in the FixedGridLayoutManager example:

public void onLayoutChildren(RecyclerView.Recycler recycler,
                             RecyclerView.State state) {
    //Scrap measure one child
    View scrap = recycler.getViewForPosition(0);
    measureChildWithMargins(scrap, 0, 0);

     * We make some assumptions in this code based on every child
     * view being the same size (i.e. a uniform grid). This allows
     * us to compute the following values up front because they
     * won't change.
    mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap);
    mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap);
    detachAndScrapView(scrap, recycler);

    int childLeft;
    int childTop;

     * Reset the visible and scroll positions
    mFirstVisiblePosition = 0;
    childLeft = childTop = 0;

    //Clear all attached views into the recycle bin
    //Fill the grid for the initial layout of views
    fillGrid(DIRECTION_NONE, childLeft, childTop, recycler);

We do some bookkeeping and setup (this manager assumes all child views from the adapter will be the same size, for simplicity), and make sure all views that might have existed are in the scrap heap. I have abstracted the bulk of the work to a fillGrid() helper method for re-use. We will see shortly this method gets called a lot to update the visible views as scrolling occurs as well.

Just as with a custom ViewGroup implementation, you are responsible for triggering measure and layout on each child view you get from the Recycler. None of this work is done directly by the APIs.

In general, the primary steps you would want to accomplish in a method like this are:

  1. Check the current offset positions of all the attached views after the latest scroll event.

  2. Determine if there are empty spaces created by scrolling where new views need to be added. Get them from the Recycler.

  3. Determine if there are now existing views that are no longer visible. Remove them and place them in the Recycler.

  4. Determine if the remaining views should be reorganized. It may be that the above changes require you modify the child indices of the views to better align with their adapter positions.

Notice the primary steps we’ve taken to fill the RecyclerView inside of FixedGridLayoutManager.fillGrid(). This manager orders positions right-to-left, wrapping when the max column count is reached:

  1. Take inventory of the views we have currently. Detach them all so they can be re-attached again later.

    SparseArray viewCache = new SparseArray(getChildCount());
    if (getChildCount() != 0) {
        //Cache all views by their existing position, before updating counts
        for (int i=0; i < getChildCount(); i++) {
            int position = positionOfIndex(i);
            final View child = getChildAt(i);
            viewCache.put(position, child);
        //Temporarily detach all views.
        // Views we still need will be added back at the proper index.
        for (int i=0; i < viewCache.size(); i++) {
  2. Do measure/layout for each child view that is visible at the current moment. Views we already had are simply re-attached; new views are obtained from the Recycler.

    for (int i = 0; i < getVisibleChildCount(); i++) {
        //Layout this position
        View view = viewCache.get(nextPosition);
        if (view == null) {
             * The Recycler will give us either a newly constructed view,
             * or a recycled view it has on-hand. In either case, the
             * view will already be fully bound to the data by the
             * adapter for us.
            view = recycler.getViewForPosition(nextPosition);
             * It is prudent to measure/layout each new view we
             * receive from the Recycler. We don't have to do
             * this for views we are just re-arranging.
            measureChildWithMargins(view, 0, 0);
            layoutDecorated(view, leftOffset, topOffset,
            leftOffset + mDecoratedChildWidth,
            topOffset + mDecoratedChildHeight);
        } else {
            //Re-attach the cached view at its new index
  3. Finally, any views we detached in Step 1 that did not get re-attached are no longer visible. Relegate them all to the Recycler for use later on.

    for (int i=0; i < viewCache.size(); i++) {

As a side note, the reason we detach all the views and re-attach just those we still need is to preserve the order of child indices (i.e. the getChildAt() index) for each view. We expect that the visible views flow from top-left as 0 to bottom-right as getChildCount()-1. As we scroll in both directions and new children are attached, this ordering would become unreliable. We need this order to be preserved to best determine the location of each child at any point. In a simpler LayoutManager (like LinearLayoutManager) where child views can be easily inserted at each end of the list, this level of bookkeeping isn’t necessary.

Adding User Interactivity

At this stage we have a very nice initial layout, but no way to move around. The whole point of a RecyclerView is to dynamically provide views as the user scrolls through a data set! A few more overrides will get us there.

canScrollHorizontally() & canScrollVertically()

These methods are simple. Just return true if your view supports any scrolling at all in the given direction, and false if you want to ignore it.

public boolean canScrollVertically() {
    //We do allow scrolling
    return true;

scrollHorizontallyBy() & scrollVerticallyBy()

Here you will implement the logic by which the content should shift. Scrolling and flinging touch logic is handled already by RecyclerView, so no messing with MotionEvents or GestureDetectors. You have three tasks inside of these methods:

  1. Shift all child views by the appropriate distance (yes, you have to do this part yourself).

  2. Determine if another fill is in order to add/remove views once we’ve shifted.

  3. Return back the actual distance traveled. The framework uses this to know when you’ve hit an edge boundary.

In FixedGridLayoutManager, both methods are very similar. Here is the condensed implementation of scrolling vertically:

public int scrollVerticallyBy(int dy,
                              RecyclerView.Recycler recycler,
                              RecyclerView.State state) {

    if (getChildCount() == 0) {
        return 0;

    //Take top measurements from the top-left child
    final View topView = getChildAt(0);
    //Take bottom measurements from the bottom-right child.
    final View bottomView = getChildAt(getChildCount()-1);

    //Optimize the case where the entire data set is too small to scroll
    int viewSpan = getDecoratedBottom(bottomView) - getDecoratedTop(topView);
    if (viewSpan <= getVerticalSpace()) {
        //We cannot scroll in either direction
        return 0;

    int delta;
    int maxRowCount = getTotalRowCount();
    boolean topBoundReached = getFirstVisibleRow() == 0;
    boolean bottomBoundReached = getLastVisibleRow() >= maxRowCount;

    if (dy > 0) { // Contents are scrolling up
        //Check against bottom bound
        if (bottomBoundReached) {
            //If we've reached the last row, enforce limits
            int bottomOffset;
            if (rowOfIndex(getChildCount() - 1) >= (maxRowCount - 1)) {
                //We are truly at the bottom, determine how far
                bottomOffset = getVerticalSpace()
                        - getDecoratedBottom(bottomView) + getPaddingBottom();
            } else {
                 * Extra added to account for allowing bottom space in the grid.
                 * Occurs when the overlap in the last row is not large enough to
                 * ensure that at least one element in that row isn't recycled.
                bottomOffset = getVerticalSpace()
                        - (getDecoratedBottom(bottomView) + mDecoratedChildHeight)
                        + getPaddingBottom();
            delta = Math.max(-dy, bottomOffset);
        } else {
            //No limits while the last row isn't visible
            delta = -dy;
    } else { // Contents are scrolling down
        //Check against top bound
        if (topBoundReached) {
            int topOffset = -getDecoratedTop(topView) + getPaddingTop();
            delta = Math.min(-dy, topOffset);
        } else {
            delta = -dy;


    if (dy > 0) {
        if (getDecoratedBottom(topView) < 0 && !bottomBoundReached) {
            fillGrid(DIRECTION_DOWN, recycler);
        } else if (!bottomBoundReached) {
            fillGrid(DIRECTION_NONE, recycler);
    } else {
        if (getDecoratedTop(topView) > 0 && !topBoundReached) {
            fillGrid(DIRECTION_UP, recycler);
        } else if (!topBoundReached) {
            fillGrid(DIRECTION_NONE, recycler);

     * Return value determines if a boundary has been reached
     * (for edge effects and flings). If returned value does not
     * match original delta (passed in), RecyclerView will draw
     * an edge effect.
    return -delta;

Notice we are given the incremental scroll distance (dx/dy) to validate. The first portion of this method deals with determining whether scrolling the given distance (the sign gives the direction) would overscroll the edge of our content. If it would, we need to reduce how far the views are actually moved.

We must manually move the views ourselves inside of this method. The offsetChildrenVertical() and offsetChildrenHorizontal() methods assist us in applying the uniform translation. If you don’t do this, your views won’t scroll. After moving the views, we trigger another fill operation to swap views based on the direction we scrolled.

Finally, we return the actual shift value applied to the children. RecyclerView uses this value to determine when it should draw the edge effects you see on any scrolling content when the end is reached. Essentially, if the return value doesn’t exactly match the dx/dy passed in, expect some amount of edge glow to be drawn. Also note that if you return a value with the incorrect sign, the framework’s math will take that also as a big change and you’ll get edge glows at the wrong time.

In addition to drawing edge effects, this return value is also used to determine when to cancel flings. Returning the incorrect value here will disable your ability to fling the content as the framework will think you’ve prematurely hit an edge and abort the fling.

Just Getting Warmed Up

At this point, we should have a base functional implementation. It’s missing some of the finer details, but scrolling and proper view recycling are in place. There’s a lot more to go in our discussion of building a custom LayoutManager. In the next segment, we will look at dealing with decorations, data set changes, and implementing position scrolling.



Dave Smith is an embedded software developer based in Denver, CO and head geek Wireless Designs, LLC. He has been focused on the Android platform since 2009. If you would like to hear more from Dave, you can follow him on Twitter @devunwired. You can also find him on Google+.

  • Hi Dave, I start learning RecyclerView since you publish this awesome playground series, i facing some troubles about nesting RecyclerViews, have you capable of this subject? it’s very appreciative if you can help me in this stackoverflow question : .

  • Aung Pyae Phyo

    Hi Dave,
    I am trying to implement “overlapped items” structure by using RecyclerView. Do I need to entirely create a new LayoutManager ? or can I just extends LinearLayoutManager and enhance a few ? What I want is exactly the same as I am getting from LinearLayout except that items will be overlapping from top to bottom (maybe 50%). Like a stack of cards. I am still looking around. So, pls let me know have any idea related to RecyclerView and my requirement.

  • 星鸽王

    Hi Dave

    I’m new comer for android, I’ve learned how to use animation to control viewitem, but I really need to know is a way to control the whole recylerview move by animation.

    Cause of item animation will have a blank space between new added item and previous item.

  • Fernando Rodriguez Patiño

    hi dave
    I have a recycler view painting rows of 10 TextView and a lot of records, but when scrooll is very slow, any idea how to optimize it to make it faster

  • Dave, you are a supreme dev mate, no doubts about that, Animations are a pretty nasty topic for most people when it comes to complex views like the RecylcerView or ListView, the ItemAnimator only animates adding and removing stuff, it would be really great if you can dig into the animation mechanics and a bit of Math related to that as well, thanks for your highly informative posts

  • As with any list implementation, make sure you are doing as little work as possible when binding data to the views. You shouldn’t be fetching data from the network or disk, for instance, in onBindViewHolder() (or even onCreateViewHolder(), really).

  • Stephen Talley

    Thanks for the very thorough overview, Dave. I am wondering what your thoughts are on the best way to amend an existing LayoutManager to implement sticky grid headers? All the approaches I’ve seen out there use ItemDecoration.onDrawOver to implement this, but that approach doesn’t allow for an actual header View to handle clicks, etc. I’d prefer to specify that some subset of the Views created in the Adapter are header Views, and somehow extend an existing LayoutManager (GridLayoutManager, say) to prevent it from scrolling a header View until another header View pushes it out of the way. Thanks for your insight!

  • RecyclerView doesn’t really have a good mechanism (yet) for handling these types of “extra” views (i.e. views that don’t really have a data position but need to be in the layout). RecyclerView adapters do support view types, so placing headers inline with your data items is possible. However, the view types are not exposed to the layout manager (and LayoutManager is really designed not to touch the adapter directly), so it’s hard to make judgements in your layout based on type. You could pack additional data in the ViewHolder indicating a header view, and reacting to any attached header views inside of onLayoutChildren(), scrollXXXBy(), etc.

    Also, keep in mind that standard layout managers assume all attached views can be shifted uniformly, so they would try to move any header views for you. You would have to account for that in your extension.

  • Vikas Pandey

    Hi Dave ,i will be very thankful to you ,if you once just take a look at my question in stack overflow .

  • Abbas Anwer Ali

    Hi Dave, I had implemented nested RecyclerView well enough but I also needed to add Synced Scrolling for RecyclerViews but doing so gives a StackOverflowError. So I decided to use a single RecylerView and to make my own LayoutManager. Kind of like Fixed-Two-WayLayout in the sample code but a little different.
    So I have this Table-like view and each item has its own width and each row has its own number of columns. And I need Sync Scrolling in both directions.
    My question is how would you suggest going about this problem?

  • Marcin

    I am looking for exactly the same functionality as you. Did you find something what you could share ?

  • I haven’t tried it, but you may be able to accomplish this with an ItemDecoration that shifts the bounds of each view to overlap. If not, then yes, a custom LayoutManager would be required. Extending LinearLayoutManager might be difficult because it’s FILL method is not public, but you could copy the code and start making the changes you need from there.

  • Punit

    Hi Dave,

    Thanks a lot for superb tutorials.

    I am using simple rows layout with spinner, autocomplete textview and couple of edit text. I am just doing some loops in onBindViewHolder to bind the data. Not doing any network operation or any heavy work. Still I am facing this issue. While scroll it is stuttering the view.

    I tried using setHasStableIds with return NO_ID and position respectively. I observed that with NO_ID recyclerview is very smooth but i see duplicate entries. At the same time if i return object hashCode or position in getItemId method scroll becomes laggy.

    I think i need to use cache mechanism like you have created for Grid layout manager. But it is very complex to understand.

    Can you please help with that, what the real issue could be ? Is there a way to stop calling onBindViewHolder() on scroll ?
    or can you please post any example for vertical Linear Layout Manager.

  • Sounds like you need to do some profiling of you code to figure out where extra time during scrolling is being spent:

  • Punit

    Thanks Dave for quick reply.

    That was helpful, I tried using profiling and saw most cpu cycles are used by RecyclerView and LayoutManager for measuring views. I also observed when using setItemViewCacheSize(50) with recyclerView it is working fine after i scrolled to last item and start scrolling again. Because it seems RecyclerView is using cached views rather then measuring views. Can you please guide me to improve Item View rendering or measuring on scroll using Vertical Layout Manager.

    I have attached profiling image for more details.

    Thanks a lot once again.

  • I can’t really give specifics, other than to say your item view is likely too complex (too many child view or nested child views), or there is a child view that is repeatedly causing measure/layout to be called due to a change in one of it’s properties.

    As an example (I’m not saying this is happening in your case): TextView does a layout pass every time setText() is called, if the layout properties are wrap_content.

  • Abhay Mehta

    Hi Dave,

    Thanks for this tutorial! In your “onLayoutChildren” method, I see that you’ve said that you make an assumption that all children views will be the same size. I had a question regarding custom-sized cells using your FixedGridLayoutManager code.

    I’d like the width of each cell to vary based on the data.

    From my understanding, the three parts to the RecyclerView are the Adapter, the LayoutManager, and the Recycler itself. In the Adapter, I see that I have the option to create varying sized ViewHolders by inflating different views based on the appropriate viewTypes. I see that I can specify the viewType based on the position. I believe this is how its done with StaggeredGrid RecyclerViews.

    The part that I’m not understanding is how the LayoutManager (your FixedGridLayoutManager) communicates with the Adapter to get the appropriate sized view I specified in “onCreateViewHolder” (based on the viewType). My guess is that has to do with the Recycler caching children views sized with mDecoratedChildWidth, but I can’t quite figure out where this happens. If you could point me to a method or two in your FixedGridLayoutManager, that’d be greatly appreciated!

    Thanks again,
    – Abhay

  • Abhay –

    You’re right, I cheated a bit and fixed the size of each child. This allowed me to do an optimization where I pre-determine how many views fit on screen (see updateWindowSizing() in the source). This value was used during layout to know when to move to the next row each time (see this section of fillGrid(), for example).

    If the child views have variable widths (or heights, even), you just have to modify that part of layout to advance based on the getDecoratedMeasuredWidth() of each new view…rather than assuming it’s always the same value. This value is always populated because a few lines before we measure the view (if it’s new), or it’s already been measured (if it’s cached/recycled). The LayoutManager should never ask the Adapter for information. Just ask the view to measure itself and see what the measurements are.

    Hope that helps.

  • Abhay Mehta

    Hi Dave,

    Thank you for the prompt reply, it really helped. I replaced all (most?) occurrences of mDecoratedChildWidth with getDecoratedMeasuredWidth(...). This resulted in cells with different widths, like I wanted.

    While scrolling horizontally, I noticed that when the cell in top-left position was completely hidden, it would trigger a layout (detaching and scrapping) causing some views which should have been partially visible to disappear completely. These views were in the same “column” as the top-left view. When I scrolled back, there would be a space (white gap) between where the cell was and where it should have been.

    I tried brainstorming different causes. It can’t be item decorations because I’m not using any. I believe the fillGrid(...) method lays out the other cells based on the cell in the top-left position. I thought there was an optimization to not consider views which have a smaller position than the top-left view when re-laying out, but I couldn’t find it. I think it has to do with advancing the position when DIRECTION_END occurs.

    Is this a result of a pre-optimization? Would you be able to shed some light? Sorry for the plethora of questions!

    Thanks a bunch,
    – Abhay

  • You are right, the fill logic is intended to only lay out the views that are visible. When a row/column scrolls off-screen, those views should be recycled for re-use. This logic works for fixed size views, because it assumes the views can be added or removed in whole rows and columns. For a variable size views, you will probably need to make the fill operation smarter about each view and it’s actual position.

  • Zell

    Nice comments ! Just to add my thoughts , if your company is requiring a IRS 1040 – Schedule E , my business partner came across a fillable form here

  • Davidoff

    Hello Stephen, did you succeed to have a LayoutManager for sticky headers?

  • Stephen Talley

    I ended up looking at third-party libraries, such as superslim for this piece. Rolling our own layout manager was just too much of a pain.

  • Davidoff

    I know that project, superSlim has 3400 lines only for sections, it is too much. Maybe you can show me the point where the View is chosen to become sticky in that project? I can try to replicate that part.

    In my FlexibleAdapter project I’ve found 2 others ways to make sticky a header: A decoration, worked but no click is possible so I deprecated it; and a FrameLayout works very well with clicks too, but I have a bug when collapsing the section because the ViewHolder is not part of the RecyclerView. So I’m still convinced that the correct way is a LayoutManager but it is difficult to implement, it is not clear enough to me this component, even if this articles gives some help.

  • Dominik Jura

    Hi Dave,
    Thanks for really good tutorial. I wonder if it’s possible to solve my problem on Stack with RecycleView. I would be really glad if You could look at it and help:

  • RecyclerView would only make sense for you if the layout structure you are creating is intended to be larger than the display (i.e. you need to scroll around the layout because it isn’t all visible at once). If you are laying out all of your views at the same time, RecyclerView is unnecessary complexity and you should just put your logic into a custom View or ViewGroup.

  • Dominik Jura

    Thanks for quick respond. I am thinking about really large number of bitmaps so the scroll will be necessary. Already I wrote arrangement of objects using layoutDecorated() and it look pretty good. I wonder how difficult will be writing scroll logic and what about animations deleting object?

  • The scrolling logic is quite simple, just shift the views based on the given delta unless you hit the edge. What can get difficult is the fill logic you employ to dynamically attach/detach views as you scroll (where the performance gains of recycling come from). Your logic will probably be similar to mine, since it’s hard to make any assumptions about what views to add based on scrolling. Easiest to 1) Detach All, 2) Run a full layout pass, 3) Recycle anything that wasn’t reused between #1 and #2.

    Animations are a different beast. As you can see from these posts, I had a similar problem animating a non-linear layout and ran into some limitations of the framework that haven’t been updated AFAIK. You might need to settle for the standard non-predictive animations if you want to animate structural changes. It might not be ideal, but it will likely be understandable for the user.

  • Ali Mehrpour

    Hi Dave,
    Thanks for your amazing article.

    I just confused about calling fillGrid(DIRECTION_NONE, recycler) in scrollVerticallyBy() and scrollHorizontallyBy() methods. If I understood correctly when we scroll the RecyclerView, if it doesn’t need to load new views into screen, offsetChildrenVertical(delta) and offsetChildrenHorizontal(delta) methods will move the views on the screen. So why need to fill grid again in these cases?

  • Precautionary measure, mostly. The fill logic in this example also handles scrapping and recycling views that are no longer visible. In cases where movement occurs and we haven’t hit the edge, an existing view may have scrolled off-screen that we can get rid of…even if we can’t fill in any new views yet.