RecyclerView and SwipeRefreshLayout

write the following code in addOnScrollListener of the RecyclerView

Like this:

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            int topRowVerticalPosition =
                    (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();
            swipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0);

        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }
    });

Before you use this solution: RecyclerView is not complete yet, TRY NOT TO USE IT IN PRODUCTION UNLESS YOU'RE LIKE ME!

As for November 2014, there are still bugs in RecyclerView that would cause canScrollVertically to return false prematurely. This solution will resolve all scrolling problems.

The drop in solution:

public class FixedRecyclerView extends RecyclerView {
    public FixedRecyclerView(Context context) {
        super(context);
    }

    public FixedRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FixedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean canScrollVertically(int direction) {
        // check if scrolling up
        if (direction < 1) {
            boolean original = super.canScrollVertically(direction);
            return !original && getChildAt(0) != null && getChildAt(0).getTop() < 0 || original;
        }
        return super.canScrollVertically(direction);

    }
}

You don't even need to replace RecyclerView in your code with FixedRecyclerView, replacing the XML tag would be sufficient! (The ensures that when RecyclerView is complete, the transition would be quick and simple)

Explanation:

Basically, canScrollVertically(boolean) returns false too early,so we check if the RecyclerView is scrolled all the way to the top of the first view (where the first child's top would be 0) and then return.

EDIT: And if you don't want to extend RecyclerView for some reason, you can extend SwipeRefreshLayout and override the canChildScrollUp() method and put the checking logic in there.

EDIT2: RecyclerView has been released and so far there's no need to use this fix.


I came across the same problem recently. I tried the approach suggested by @Krunal_Patel, But It worked most of the times in my Nexus 4 and didn't work at all in samsung galaxy s2. While debugging, recyclerView.getChildAt(0).getTop() is always not correct for RecyclerView. So, After going through various methods, I figured that we can make use of the method findFirstCompletelyVisibleItemPosition() of the LayoutManager to predict whether the first item of the RecyclerView is visible or not, to enable SwipeRefreshLayout.Find the code below. Hope it helps someone trying to fix the same issue. Cheers.

    recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        }

        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            swipeRefresh.setEnabled(linearLayoutManager.findFirstCompletelyVisibleItemPosition() == 0);
        }
    });

This is how I have resolved this issue in my case. It might be useful for someone else who end up here for searching solutions similar to this.

recyclerView.addOnScrollListener(new OnScrollListener()
    {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            // TODO Auto-generated method stub
            super.onScrolled(recyclerView, dx, dy);
        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState)
        {
            // TODO Auto-generated method stub
            //super.onScrollStateChanged(recyclerView, newState);
            int firstPos=linearLayoutManager.findFirstCompletelyVisibleItemPosition();
            if (firstPos>0)
            {
                swipeLayout.setEnabled(false);
            }
            else {
                swipeLayout.setEnabled(true);
            }
        }
    });

I hope this might definitely help someone who are looking for similar solution.


Source Code https://drive.google.com/open?id=0BzBKpZ4nzNzURkRGNVFtZXV1RWM

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    }

    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        swipeRefresh.setEnabled(linearLayoutManager.findFirstCompletelyVisibleItemPosition() == 0);
    }
});