Android SwipeRefreshLayout Tutorial

In this post, we will describe Android SwipeRefreshLayout component. This component should be used whenever the user can refresh the UI using swipe gesture. In a previous post, we talked about another method to refresh the UI that we called shake to refresh, where the user shakes his smartphone and using accelerometer sensor the app refresh the UI. We talked about a custom implementation of this refresh pattern in this post where we implemented some like it.
The Android SwipeRefreshLayout component is a standard implementation provided by the SDK and it is already used in some App provided by Android (i.e Gmail).

Android swiperefreshlayout tutorial

Getting started with  Android SwipeRefreshLayout

Android SwipeRefreshLayout component accepts only one child: the component we want to refresh. It uses a listener mechanism to inform the listener that holds this component that a refresh event occurred, so in other word our Activity, for example, has to implement an interface to be notified. The Activity is responsible to handle the refresh event and refreshing the corresponding View. If the listener, once it receives the event, determines that the refresh process should take place and wants to show a “refresh animation”, it has to call setRefrshing(true), otherwise it can cancel the animation calling setRefreshing(false).

How to use SwipeRefreshLayout

Now we know how this component works, we will create a simple example to show how to use it. Let us suppose we want to generate a random number as the user uses a vertical swipe gesture:
android swipe to refresh layout

Usually this component is the root component:

<android.support.v4.widget.SwipeRefreshLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:id="@+id/swipe">
  <ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <RelativeLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent">

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Random number:"
      android:id="@+id/lbl"/>

   <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/rndNum"
      android:layout_toRightOf="@id/lbl"/>

   <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="@id/lbl"
      android:layout_centerHorizontal="true"
      android:layout_marginTop="20dp"
      android:text="Swipe to Refresh"
      style="@android:style/TextAppearance.Medium"/>

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

As you can see from the layout above, SwipeRefreshLayout has only one child. Now we can code our Activity:

...
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  final SwipeRefreshLayout swipeView = (SwipeRefreshLayout) findViewById(R.id.swipe);
  final TextView rndNum = (TextView) findViewById(R.id.rndNum);
  swipeView.setColorScheme(android.R.color.holo_blue_dark, 
                           android.R.color.holo_blue_light, 
                           android.R.color.holo_green_light, 
                           android.R.color.holo_green_light);
  swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
     @Override
     public void onRefresh() {
        swipeView.setRefreshing(true);
        Log.d("Swipe", "Refreshing Number");
        ( new Handler()).postDelayed(new Runnable() {
             @Override
             public void run() {
               swipeView.setRefreshing(false);
               double f = Math.random();
               rndNum.setText(String.valueOf(f));
            } 
      }, 3000);
   }
 });
}
....

As you can see everything in our example happens in onCreate method. At line 6 we get the reference to the SwipeRefreshLayout so that we can set the listener (line 10,11,12). In the listener, we simply can setRefreshing(true) to enable to refreshing animation and then we generate our random number. At the end (we emulate a quite long process) we stop the animation.

SwipeRefreshLayout with ListView

Another interesting example is how to use SwipeRefreshLayout with a ListView. This is an interesting example because in real app we have often this situation where we have a ListView holding some items and we want to refresh them. If the ListView is the only one child of the SwipeRefreshLayout we wouldn’t have any kind of problems, because everything works smoothly. In some cases we have not only the ListView but we have other elements, let us suppose we have an UI like this:

android swipe to refresh layout listview

This case is a little bit more complex, because if we scroll up the items in the ListView everything works as expected, but if the scroll down the refresh process starts and the list items doesn’t scroll as we want. In this case we can use a trick, we can disable the refresh notification using setEnabled(false) and enable it again as soon as the first item in the ListView is visible:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  final SwipeRefreshLayout swipeView = (SwipeRefreshLayout) findViewById(R.id.swipe);

  swipeView.setEnabled(false);
  ListView lView = (ListView) findViewById(R.id.list);
  ArrayAdapter<String> adp = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, createItems(40,0 )); 
  lView.setAdapter(adp);

  swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
     @Override
     public void onRefresh() {
      swipeView.setRefreshing(true);
      ( new Handler()).postDelayed(new Runnable() {
       @Override
       public void run() {
         swipeView.setRefreshing(false);
       }
    }, 3000);
   }
 });

 lView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
    }

    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
     if (firstVisibleItem == 0)
       swipeView.setEnabled(true);
     else
       swipeView.setEnabled(false);
    }
  });
}

As you can see at line 33, we override the onScrollListener of the ListView to handle the enable/disable mechanism.

  • Steve

    Hello hope you are having a good day, I know this is a bit off topic but i was wondering if you remake the upload to server app and put all of the source code in one github directory even the server side and explain how to optimize the app for a localhost server.

  • survivingwithandroid

    It could be a nice idea. I will provide a post about it. Thx for your suggestion

  • Steve

    Thank you so much

  • Matt

    Instead of adding an onScrollListener, you should subclass the SwipeRefreshLayout class and Override the canChildScrollUp method.

  • Guillaume Bourderye

    Hi !

    I think you should add these 2 conditions in your if:

    @Override

    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    if (firstVisibleItem == 0 && visibleItemCount > 0 && getListAdapter().getView(0, null, null).getTop() >= 0)
    swipeView.setEnabled(true);

    else
    swipeView.setEnabled(false);

    }

    It allows to call the refresh method only when you are at the top of your listView.

    • malav shah

      after applying the following solution, I am facing problem if the 1st row in the list view has height greater than the screen size. It wont load other rows on screen and not able to scroll. If the height of 1st row is less than the screen size, than all works as expected.

  • Voldik

    Hi, thanks a lot for the first example!

  • Keyur Lakhani

    Thnx for this example so nice , easy to understand & implement ………:)

  • i am getting the error cannot resolve symbol ‘swipe’

  • PanharithHuot

    wow wonderful. Thank!…..