question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Why SwipeRefreshLayout dont work, when first model is hidden.

See original GitHub issue

I use Epoxy to manage RecycleView inside SwipeRefreshLayout, like below:

 <android.support.v4.widget.SwipeRefreshLayout
      android:id="@+id/srLayout"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

      <android.support.v7.widget.RecyclerView
          android:id="@+id/rvList"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>
  </android.support.v4.widget.SwipeRefreshLayout>

It is weird when I hide first model of RecycleView by EpoxyModel.hide(), SwipeRefreshLayout doesn’t work anymore. I create a TestProject, https://github.com/fanxu123/EpoxyTest, specially for demonstration. Please help, thanks.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:2
  • Comments:14 (6 by maintainers)

github_iconTop GitHub Comments

8reactions
SamThompsoncommented, Nov 10, 2016

I was able to track the bug down to LinearLayoutManager.

Before showing the refresh spinner, SwipeRefreshLayout makes a check to whether or not RecyclerView can be scrolled in the negative direction:

        // from SwipeRefreshLayout#onNestedScroll
        final int dy = dyUnconsumed + mParentOffsetInWindow[1];
        if (dy < 0 && !canChildScrollUp()) {  // <-- evaluates to false and never shows the spinner 
                                              // b/c canChildScrollUp() == true
            mTotalUnconsumed += Math.abs(dy);
            moveSpinner(mTotalUnconsumed);
        }

the conditional dy < 0 && !canChildScrollUp() evaluates to false. Tracing canChildScrollUp(), it eventually leads us to this code in LinearLayoutManager:

    private int computeScrollOffset(RecyclerView.State state) {
        if (getChildCount() == 0) {
            return 0;
        }
        ensureLayoutState();
        return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
                this, mSmoothScrollbarEnabled, mShouldReverseLayout);
    }

The call to findFirstVisibleChildClosestToStart will return the first non-hidden item (since the height of a hidden item is 0dp and isn’t considered visible by the layout manager). However, because there is still technically an item before the first non-hidden item, the layout manager still considers the RecyclerView scrollable in the negative direction due to this math:

// final return in ScrollbarHelper#computeScrollOffset
return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
                - orientation.getDecoratedStart(startChild)))

Let me know if that explanation is unclear and I can do my best to clarify. I can imagine a similar thing is happening in #61

1reaction
SiarheiSmcommented, Aug 6, 2020

workaround:

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I fix this misbehavior of SwipeRefreshLayout
Halve of the loader hides behind the toolbar and the other half is visible on top the first item in recyclerview. If I...
Read more >
SwipeRefreshLayout - Android Developers
The SwipeRefreshLayout should be used whenever the user can refresh the contents of a view via a vertical swipe gesture. The activity that...
Read more >
Android Pull/Swipe Down to Refresh - DigitalOcean
Android SwipeRefreshLayout is a ViewGroup that can hold only one scrollable child. It can be either a ScrollView, ListView or RecyclerView. The ...
Read more >
Pull To Refresh ListView & RecyclerView Example In Android ...
Implementing a pull to refresh is very easy in Android. Whenever we need to detect the swipe down on any view, just wrap...
Read more >
Implementing Pull to Refresh Guide - CodePath Cliffnotes
SwipeRefreshLayout is a ViewGroup that can hold only one scrollable view as a child. This can be either a ScrollView or an AdapterView...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found