Pull to Refresh – Part 1

On 26th October 2012 I made a presentation about Android Layouts at DroidCon in London. While I was there I took in a great talk by Scott Alexander-Bown titled entitled “Write Less / Do More” in which Scott spoke about some third-party libraries for Android. One of these is Chris Banes‘ Pull-to-Refresh library, and in this article we’ll take a look at this library and see how simple it is to incorporate this in to your app.

Let’s start by creating a new 4.1.2 Android project named Pull2Refresh with the package name com.stylingandroid.pull2refresh. We’re going to set up a very basic ListView based app with some static content and a “Refresh” button in the ActionBar. We’ll start by creating a couple of simple layouts, the first is our main layout in res/layout/activity_main.xml:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

</ListView>

The second is for an indeterminate progress indicator (which we’ll use later) in res/layout/layout_progress.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="64dp"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:paddingRight="12dp" >

    <ProgressBar
        style="?android:attr/indeterminateProgressStyle"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:layout_gravity="center" />

</FrameLayout>

Hopefully they should be pretty straightforward, so do not require any explanation.

Next we’ll create our menu definition in res/menu/activity_main.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_refresh"
        android:icon="@drawable/ic_menu_refresh"
        android:showAsAction="always" />
</menu>

We’re adding a simple refresh button to the ActionBar. The only thing worth noting here is that the refresh icon is not publicly available, so what I’ve had to do is go to the Android Source repo and download the images for hdpi, ldpi, mdpi, & xhdpi and put them in the resources folder.

Next We’ll create out MainActivity which should extend ListActivity:

public class MainActivity extends ListActivity {
	private static final String[] sContent = new String[] { 
		"One", "Two", "Three", "Four", "Five", "Six", 
		"Seven", "Eight", "Nine", "Ten", "Eleven", 
		"Twelve", "Thirteen", "Fourteen", "Fifteen", 
		"Sixteen", "Seventeen", "Eighteen", "Nineteen", 
		"Twenty" };
	private ArrayAdapter<String> mAdapter;
	private MenuItem mRefresh = null;
	private Handler mHandler = new Handler();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mAdapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, android.R.id.text1,
				Arrays.asList(sContent));
		setListAdapter(mAdapter);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		mRefresh = menu.findItem(R.id.menu_refresh);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (item.getItemId() == R.id.menu_refresh) {
			item.setActionView(R.layout.layout_progress);
			mHandler.postDelayed(new Runnable() {

				@Override
				public void run() {
					refreshComplete();
				}
			}, 2000);
		}
		return super.onOptionsItemSelected(item);
	}

	private void refreshComplete() {
		mRefresh.setActionView(null);
	}

}

This should also be pretty straightforward. What we’re doing is creating a very simple Adapter to populate our ListView, and adding a dummy “refresh” behaviour which will show the indeterminate progress for 2 seconds after the refresh button is pressed. So this would be pretty traditional refresh behaviour of a ListView.

I wanted to create the basic behaviour in order to show not only how easy it is to implement Pull-to-Refresh, but also how easy it is to retro-fit it to existing code.

To add Pull-to-Refresh, we must first import the library project from GitHub. Select ‘File|Import…|Git|Projects from Git’ then hit “Next”. Then click URI and “Next” again. In the URI field enter ‘https://github.com/chrisbanes/Android-PullToRefresh.git’, and click “Next”. Select the “master” branch then click “Next”. Keep the default values on the next page, but make a note of the directory as you’ll need it shortly, and click “Next”. Select “Use the New Project wizard” then click “Finish”.

From the wizard selection, select “Android|Android project from existing code” and click “Next”. Now click “Browse” and go to the directory that you noted earlier then select the folder within it named ‘library’ and click “OK”. You should see the library project appear in the “Projects” list, and then click “Finish”.

This will check out the library, and all we need to do is add this as a library to our app by right clicking on Pull2Refresh in Package Explorer, then selecting “Properties|Android” scrolling to the bottom, clicking “Add” selecting the library project, and clicking “OK”.

Now that we have included the library, we need to make a few small changes to our app. First we need to update our main layout to use the PullToRefreshListView instead of a standard ListView:

<com.handmark.pulltorefresh.library.PullToRefreshListView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

</com.handmark.pulltorefresh.library.PullToRefreshListView>

If we try and run this it will give a CastCastException because ListActivity expects a ListView, but PullToRefreshListView is a wrapper around ListView and does not actually extend ListView. This is easy enough to fix. First we need to change MainActivity to extend Activity rather than ListActivity, then we need to change the onCreate method slightly:

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	mAdapter = new ArrayAdapter<String>(this,
			android.R.layout.simple_list_item_1, android.R.id.text1,
			Arrays.asList(sContent));
	PullToRefreshListView listView = 
		(PullToRefreshListView)findViewById(android.R.id.list);
	listView.setAdapter(mAdapter);
}

If we run that, we can see that gives us some basic pull to refresh behaviour but it’s not yet kicking off the refresh behaviour:


In the next article we’ll finish connecting it up, and look at some ways that we can customise it a little.

The source code for this article can be found here.

© 2012, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

Send the author to the moon!

Creative Commons License
Pull to Refresh – Part 1 by Styling Android, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Terms and conditions beyond the scope of this license may be available at blog.stylingandroid.com.

Tags:

8 Responses to “Pull to Refresh – Part 1”

  1. johndue says:

    The level of your blogs is very high for the new. Could you give a quick guide on how to import this Seller pulltorefresh in Eclipse? I try importing a. Git, but are importing a master project that I can not use. Thanks in advance!

  2. johndue says:

    sorry my english. “to import the library pulltorefresh”

  3. CodE07 says:

    Great article and great library!

    I’m using it in a project.

    Everything works. But Chris Banes extended listview has no methods
    onSaveInstanceState
    onRestoreInstanceState

    Therefore, I do not know how to go to the new position of loaded elements. The idea is this: When you’re at the end of the list and you pull, when refreshing the list back to the first position and I have no way to re-position the last item in the list (above the first element of the last loaded ).

    Can you help me, oh great Mark Allison? ;)

    Thanks from Spain
    David

    • Mark Allison says:

      The control does not have those methods, but neither does a standard LustView. The instance state is part of the Activity life cycle, not that of the controls themselves.

      The place to store the state is in the Activity or Fragment which holds the control, and does support these methods.

      I hope this helps.

      • CodE07 says:

        Thx Mark.

        Finally I have seen in the code of the class that Chris has provided the method:

        getRefreshableView

        Which gives access to the object within the wrapped.

        Thanks for your articles and for your interest!

  4. Eric says:

    Hi Mark,
    I followed your tutorial in viewpager where creating listview at instantiateItem and there is no need to create a XML layout for the listview.
    However in the pulltorefresh it require us to insert . Beside that, viewpager listview is within pageradapter.
    Could you show some example on how to implement this into the viewpager?
    Thanks alot

Leave a Reply