RecyclerView - Scroll To Position Not Working Every Time

Abbas picture Abbas · Apr 5, 2016 · Viewed 46k times · Source

I have implemented a horizontal scrollable RecyclerView. My RecyclerView uses a LinearLayoutManager, and the problem I am facing is that when I try to use scrollToPosition(position) or smoothScrollToPosition(position) or from LinearLayoutManager's scrollToPositionWithOffset(position). Neither works for me. Either a scroll call doesn't scroll to the desired location or it doesn't invoke the OnScrollListener.

So far I have tried so many different combinations of code that I cannot post them all here. Following is the one that works for me (But only partially):

public void smoothUserScrollTo(final int position) {

    if (position < 0 || position > getAdapter().getItemCount()) {
        Log.e(TAG, "An attempt to scroll out of adapter size has been stopped.");
        return;
    }

    if (getLayoutManager() == null) {
        Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " +
                "Call setLayoutManager with a non-null layout.");
        return;
    }

    if (getChildAdapterPosition(getCenterView()) == position) {
        return;
    }

    stopScroll();

    scrollToPosition(position);

    if (lastScrollPosition == position) {

        addOnLayoutChangeListener(new OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

                if (left == oldLeft && right == oldRight && top == oldTop && bottom == oldBottom) {
                    removeOnLayoutChangeListener(this);

                    updateViews();

                    // removing the following line causes a position - 3 effect.
                    scrollToView(getChildAt(0));
                }
            }
        });
    }

    lastScrollPosition = position;
}

@Override
public void scrollToPosition(int position) {
    if (position < 0 || position > getAdapter().getItemCount()) {
        Log.e(TAG, "An attempt to scroll out of adapter size has been stopped.");
        return;
    }

    if (getLayoutManager() == null) {
        Log.e(TAG, "Cannot scroll to position a LayoutManager is not set. " +
                "Call setLayoutManager with a non-null layout.");
        return;
    }

//      stopScroll();

        ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(position, 0);
//        getLayoutManager().scrollToPosition(position);
    }

I opted for scrollToPositionWithOffset() because of this but the case perhaps is different as I use a LinearLayoutManager instead of GridLayoutManager. But the solution does work for me too, but as I said earlier only partially.

  • When the call to scroll is from 0th position to totalSize - 7 scroll works like a charm.
  • When scroll is from totalSize - 7 to totalSize - 3, First time I only scroll to 7th last item in the list. The second time however I can scroll fine
  • When scrolling from totalSize - 3 to totalSize, I start getting unexpected behavior.

If anyone has found a work around I'd Appreciate it. Here's the gist to my code of custom ReyclerView.

Answer

Robert Banyai picture Robert Banyai · Apr 5, 2016

I had the same issue some weeks ago, and found only a really bad solution to solve it. Had to use a postDelayed with 200-300ms.

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        yourList.scrollToPosition(position);
    }
}, 200);

If you found a better solution, please let me know! Good luck!