From Punched Cards to Prompts
AndroidIntroduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
One of the great ideas formalized in the new Material Design user interface guidelines is the Swipe to Refresh UI pattern. In fact, you’ve probably already seen and used it. It’s found its way into many popular Android apps like Facebook, Google Newsstand, Trello, Gmail and many others.
Here’s what it looks like:
The Swipe to Refresh pattern is a nice fit for adapter-backed views (RecyclerView and ListView, for example) that also need to support user-requested refreshes, like a list that displays a Twitter newsfeed that’s updated by a user, on demand.
Introduced alongside KitKat and enhanced with the Lollipop release of the v4 Android support library, a working implementation of the Swipe to Refresh UI pattern is included, called SwipeRefreshLayout. All we have to do is set it up! For your reference, the project we’ll build is available for download on GitHub here.
We begin implementing the Swipe to Refresh pattern with a brand new Android Studio project and the most recent version of the Android Support Library (your SDK manager should show an Android Support Library version of at least 21.0).
The first thing we need to do is add the support library to our application€’s build.gradle
file.
compile 'com.android.support:support-v4:21.0.+'
Gradle sync your project and open the layout file that was generated when we created our first Activity from the new project wizard, named res/layouts/activity_main.xml
. We’re going to add a ListView and a SwipeRefreshLayout widget to the layout file. The ListView will display content we want to update using the Swipe to Refresh pattern, and the SwipeRefreshLayout widget will provide the basic functionality.
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/activity_main_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="@+id/activity_main_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
Notice that ListView is nested within the SwipeRefreshLayout. Any time we swipe the ListView beyond the edge of the SwipeRefreshLayout, the SwipeRefreshLayout widget will display a loading icon and trigger an onRefresh event. This event is a hook for adding our own on-demand data refresh behavior for the list.
Now that we’ve got our layout file ready, let’s set up a simple data adapter. In real life, our adapter would likely be backed by a web service, remote API or database, but to keep things simple we’ll fake a web API response. Include the following XML snippet in your res/strings.xml
file:
<string-array name="cat_names">
<item>George</item>
<item>Zubin</item>
<item>Carlos</item>
<item>Frank</item>
<item>Charles</item>
<item>Simon</item>
<item>Fezra</item>
<item>Henry</item>
<item>Schuster</item>
</string-array>
and set up an adapter to fake out new responses from the “cat names web API” for our experiment.
class MainActivity extends Activity {
ListView mListView;
SwipeRefreshLayout mSwipeRefreshLayout;
Adapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acivity_main);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_main_swipe_refresh_layout);
mListView = findViewById(R.id.activity_main_listview);
mListView.setAdapter(new ArrayAdapter<String>(){
String[] fakeTweets = getResources().getStringArray(R.array.fake_tweets);
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, fakeTweets);
mListview.setAdapter(mAdapter);
});
}
}
Now that our adapter is set up, we can wire up the refresh triggered by swiping down. We’ll get an animated loading indicator already “for free”; we just need to define what happens to our ListView. We will do that by defining the SwipeRefreshLayout widget’s expected OnRefreshListener interface. We’ll also simulate getting new data back from the CatNames webservice (we’ll build a method called getNewCatNames()
that will build an array of randomly shuffled fake responses from the cat names API).
@Override
public void onCreate(Bundle savedInstanceState) {
...
listView.setAdapter();
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshContent();
...
}
// fake a network operation's delayed response
// this is just for demonstration, not real code!
private void refreshContent(){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, getNewTweets());
mListView.setAdapter(mAdapter);
mSwipeRefreshLayout.setRefreshing(false);
});
}
// get new cat names.
// Normally this would be a call to a webservice using async task,
// or a database operation
private List<String> getNewCatNames() {
List<String> newCatNames = new ArrayList<String>();
for (int i = 0; i < mCatNames.size(); i++) {
int randomCatNameIndex = new Random().nextInt(mCatNames.size() - 1);
newCatNames.add(mCatNames.get(randomCatNameIndex));
}
return newCatNames;
}
Notice the last line in refreshContent()
in the previous listing: setRefreshing(false);
. This notifies the SwipeRefreshLayout widget instance that the work we wanted to do in onRefresh
completed, and to stop displaying the loader animation.
Now try running your app. Try swiping the ListView down and verify that the SwipeRefreshLayout loading icon displays, is dismissed, and that new tweets are loaded into the ListView. Congratulations! You’ve implemented the Swipe to Refresh pattern in your app.
You can also customize SwipeRefreshLayout’s appearance. To define your own custom color scheme to use with SwipeRefreshLayout’s animated loading icon, use the appropriately named setColorSchemeResources()
method. This takes a varargs of color resource ids.
First, define colors you want to appear in the SwipeRefreshLayout’s animated loader:
<resources>
<color name="orange">#FF9900</color>
<color name="green">#009900</color>
<color name="blue">#000099</color>
</resources>
Then call setColorSchemeResources(R.color.orange, R.color.green, R.color.blue);
in the onCreate
portion of your activity, after the SwipeRefreshLayout is loaded. Deploy your program and notice the customized colors the swipe animation now uses cat names, with color!
SwipeRefreshLayout will rotate through the colors we provided as the loader continues to be displayed.
As you can see from this simple example, Swipe to Refresh is a great pattern for simplifying the problem of user-requested data updates in your app. For more info about available API options for SwipeRefreshLayout, check out the official Android documentation page.
Introduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Jetpack Compose is a declarative framework for building native Android UI recommended by Google. To simplify and accelerate UI development, the framework turns the...
Big Nerd Ranch is chock-full of incredibly talented people. Today, we’re starting a series, Tell Our BNR Story, where folks within our industry share...