Android Core

Android SwipeRefreshLayout Tutorial

In this post, we will describe 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 SwipeRefreshLayout component is a standard implementation provided by the SDK and it is already used in some App provided by Android (i.e Gmail).

Introduction

This component accepts only one child: the one 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_swipetorefreshlayout[5]

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_swipetorefreshlayout_listview[4]

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.

Reference: Android SwipeRefreshLayout Tutorial from our JCG partner Francesco Azzola at the Surviving w/ Android blog.

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Tim
Tim
9 years ago

Instead of overriding the onScrollListener and handling disabling and enabling the swipeView inside of the onScroll method, you should be extending SwipeRefreshLayout in your View class and overriding canChildScrollUp().

Back to top button