Setting up a SwipeRefreshLayout is pretty easy. There are great tutorials out there. Even more if you already played around with other compat components like the DrawerLayout.

You just wrap your ScrollView or ListView, a few wiring here and there, and it's done. Works flawlessly, with just a few lines of code.

But what if you want to use a ListView, but you need something else besides a ListView as SwipeRefreshLayout's only child?

Maybe you want to use an empty view for all those times the list comes up empty. Or you want to put any extra view, like a SmoothProgressBar anywhere. Or a loading spinner at the center of the screen while it's loading.

Image a layout like this one.

<android.support.v4.widget.SwipeRefreshLayout  
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@string/guides_no_results"
            android:id="@+id/empty_view"
            android:gravity="center"
            android:padding="16dp"
            android:fontFamily="sans-serif-light"
            android:textSize="20sp"
            android:visibility="gone"/>


        <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:drawSelectorOnTop="true"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp"
            android:id="@+id/guides_list" />

    </LinearLayout>

</android.support.v4.widget.SwipeRefreshLayout>  

If you use this as-is, everytime you scroll up in that view, the SwipeRefreshLayout fires and updates, making your app unable to scroll up in a list.

The trick here is to wire the OnScrollListener from the ListView manually. You just check if the first row being shown matches the first top-most position, and then enable the SwipeRefreshLayout. Otherwise, disable it.

guidesList.setOnScrollListener(new AbsListView.OnScrollListener() {  
  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {

  }

  @Override
  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    int topRowVerticalPosition = 
      (guidesList == null || guidesList.getChildCount() == 0) ? 
        0 : guidesList.getChildAt(0).getTop();
    swipeContainer.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
  }
});

With this little snippet now it works perfectly.

Happy coding!

About the author
comments powered by Disqus