Android Pull To Refresh using SwipeRefreshLayout

In this tutorial we are going to learn how to implement android pull to refresh using SwipeRefreshLayout. Android pull to refresh has been used in many popular android applications like the Google Gmail and Yahoo Mail.

It is use to refresh or check for data update in a list of data that can change anytime. It involves swiping down and letting the app check for new data. If there is new data, it will repopulate the display View through an adapter binding.

The SwipeRefreshLayout was added to android support library version 4 and it has made working with pull to refresh easier.

In our project, we are going to use a ListView and we will wrap our ListView with a SwipeRefreshLayout.

Our data source will be an array of string data, if you are interested in learning how to implement this feature in android project using a remote data from external server or API, I will suggest you read my tutorial on Android ListView Pull Down to Refresh using Remote Data Source.

We are going to create an adapter that will bind our data source to our ListView widget. The adapter class will inflate a layout file which will represent the UI of each item of our ListView. The layout file will contain two TextView controls – the first TextView will display the item numbers while the second TextView will display the string values stored in a List object.

Right now it is important for us to understand what we are planning to achieve in this tutorial. The screen-shot of our project is shown below.

android pull to refresh

Before we start, it is important for you to understand the tools and environment I used in this android tutorial. Feel free to use any tools you are familiar with.

Windows 7

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, following the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidPullToRefresh

Package: com.inducesmile.androidpulltorefresh

Select Blank Activity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

If you are using Android Studio as your choice IDE, the new project will create a default

MainActivity.java file and its corresponding layout file.

The default layout file is the activity_main.xml. Open you layout file and add all the required View components that we need. The code sample for our layout file is as shown below.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/pull_to_refresh"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:background="@color/colorAccent"
        android:gravity="center|center_horizontal"
        android:textSize="16sp"
        android:padding="12dp"/>
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_below="@+id/title"
        android:layout_marginTop="8dp"
        android:layout_height="wrap_content">
        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none" />
    </android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>

In the xml file above, we have just added a ListView control which is wrapped by a SwipeRefreshLayout. There is also one TextView widget which contains instruction about swipe down and refresh.

Lets move on to create our list item layout. Create a new layout file inside the Layout folder and name it list_item.xml. We are going to add 2 TextView controls side by side to eachother. In order to achieve this, the TextViews will have a RelativeLayout ViewGroup as parent. The code snippet for this file is shown below.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="8dp">

    <TextView
        android:id="@+id/number_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:textStyle="bold"
        android:textSize="16sp"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:text="1"
        android:background="@color/colorAccent"
        android:padding="8dp" />

    <TextView
        android:id="@+id/data_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:textStyle="bold"
        android:textSize="16sp"
        android:padding="8dp"
        android:text="@string/app_name"
        android:layout_marginLeft="8dp"
        android:layout_toRightOf="@+id/number_count" />

</RelativeLayout>

Next, we will create an adapter which we will use to bind our data source to our ListView widget. Create a new java class and name it StringAdapter.java. The StringAdapter class will extends Android BaseAdapter and some methods of this class will be overridden. This class will inflate the layout file we created before and will take two parameters in its constructor. The code for the adapter class is shown below.

package com.inducesmile.androidpulltorefresh;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;

public class StringAdapter extends BaseAdapter {
    private LayoutInflater lInflater;
    private List<String> listStorage;
    
public StringAdapter(Context context, List<String> customizedListView) {
        lInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        listStorage = customizedListView;
    }
    @Override
    public int getCount() {
        return listStorage.size();
    }
    @Override
    public Object getItem(int position) {
        return listStorage.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder listViewHolder;
        if(convertView == null){
            listViewHolder = new ViewHolder();
            convertView = lInflater.inflate(R.layout.list_item, parent, false);
            listViewHolder.numberCount = (TextView)convertView.findViewById(R.id.number_count);
            listViewHolder.dataContent = (TextView)convertView.findViewById(R.id.data_content);
            convertView.setTag(listViewHolder);
        }else{
            listViewHolder = (ViewHolder)convertView.getTag();
        }
        listViewHolder.numberCount.setText(String.valueOf(position + 1));
        listViewHolder.dataContent.setText(listStorage.get(position));
        return convertView;
    }
    static class ViewHolder{
        TextView numberCount;
        TextView dataContent;
    }
}

The adapter class also made use of the View Holder pattern to instantiate and bind data to the View components of our layout file.

Finally, we will go over to our MainActvity.java file. In this class, we will get the instances of our View and initialize our data source. An object of our adapter class is created and it is passed as a parameter to setAdapter() method of our ListView object.

Another important code we provided in this class is a Handler object. The SwipeRefreshLayout object method postDelay() is called in onRefresh() method . This method is used to post a runnable to our Handler object which serves as a means of communication with the UI Thread. The code snippet of this class is show below.

package com.inducesmile.androidpulltorefresh;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener{
    private ListView mListView;
    private SwipeRefreshLayout mSwipeRefreshLayout;
    private List<String> mObjectList;
    private StringAdapter mStringAdapter;
    
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mObjectList = new ArrayList<String>();
        mObjectList.add("Sweden");
        mObjectList.add("Denmark");
        mObjectList.add("Finland");
        mObjectList.add("Norway");
        mObjectList.add("Iceland");
        mListView = (ListView)findViewById(R.id.listView);
        mSwipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_refresh_layout);
        mStringAdapter = new StringAdapter(MainActivity.this, mObjectList);
        mListView.setAdapter(mStringAdapter);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        mSwipeRefreshLayout.setDistanceToTriggerSync(30);
        mSwipeRefreshLayout.setSize(SwipeRefreshLayout.DEFAULT);
    }
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //super.handleMessage(msg);
            Collections.shuffle(mObjectList);
            mStringAdapter = new StringAdapter(MainActivity.this, mObjectList);
            mListView.setAdapter(mStringAdapter);
            mSwipeRefreshLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            }, 1000);
        }
    };
    @Override
    public void onRefresh() {
        mSwipeRefreshLayout.postDelayed(new Runnable() {
            @Override
            public void run() {
                mSwipeRefreshLayout.setRefreshing(true);
                mHandler.sendEmptyMessage(0);
            }
        }, 1000);
    }
}

This brings us to the end of this tutorial, If you find anything confusing kindly contact me with your questions or use the comment box below.

Now, when you run your application you will see the interface that looks similar to the sample that was shown earlier on.

You can download the code for this tutorial below. If you are having hard time downloading the tutorials, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive our new post once it is published.

If you like this tutorial, please kindly download my new android app – Daily Pocket Calculator – in Google Play Store and let me know what needs to be improved.

OTHER INTERESTING POSTS:

One Response

Add a Comment