Cyril Mottier

β€œIt’s the little details that are vital. Little things make big things happen.” – John Wooden

"Pull-to-refresh": An Anti UI Pattern on Android

Note: This article has been written prior to the recent article on Techcrunch which states “pull-to-refresh” has been patented by Twitter. I agree with most of this article when it comes to iOS. My only disagreement is I don’t think this patent is the reason why Apple didn’t use it in their applications …

As an iOS & Android developer/user I spend a lot of time looking at and playing with mobile applications. Since 2007 (the year mobile really had a leap forward thanks to the iPhone), mobile UI patterns have been introduced, enhanced and reused by applications. The Android platform has always been a very productive environment for UI designers and recently has become even more so. This rapid growth and evolution is probably a direct consequence of the increasing number of Android developers and applications. Recently, I have noticed some emerging UI patterns. Among them is the well known “pull-to-refresh” pattern.

As far as I know, “pull-to-refresh” has been primarily developed on iOS. It consists of adding an additional cell at the top of your ListView that remains hidden most of the time. As suggested by its name, you can pull down your ListView to make the widget appear. When you pull enough, a smooth animation indicates you can release the ListView in order to launch the refresh process. The cell contents switches to an activity indicator for the duration of the refresh. When the refreshing has been completed, the list scrolls in order to hide the additional cell. The screenshots below show you the complete interaction model. Please note I have used the official Twitter application. However, it doesn’t mean I appreciate this application. To be honest, I think it is in my top 10 of the least Androidy or should I say the most iOSy on Android … From my point of view, Twitter on Android is exactly the kind of app you shouldn’t do (grouped UITableView, UINavigationBar, UITabBar, UISegmentedControl, iOS look ‘n feel, etc.).

To be honest, I love this UI pattern on iOS. I think it is extremely “natural”: most recent items are usually on top of the list so it’s natural to scroll further to the top to access to the most recent items. It totally fits iOS platform philosophy. When it comes to Android, lots of developers and companies have a tendency to reproduce this pattern on the Android version of their application. I consider it a huge mistake and here is my point of view:

  • The ‘pull-to-refresh’ UI pattern is mainly dedicated to power users. I honestly don’t think it is as intuitive as a simple but clear “Refresh” button. Lots of developers use this UI pattern without thinking about their actual audience and how technophile they are. Most of the time a “Refresh” button would have been way easier to implement from a developer perspective and to use from a user point of view. I believe this is one of the main reasons Apple never used it in any of their stock iOS applications.

  • A lot of people primarily consider ‘pull-to-refresh’ as a way to save space on screen. I can’t disagree with this argument! It obviously saves space as nothing is visible on screen at all! Hiding the essential refresh button from normal users will probably just leave room for another useless button. On iOS, the framework forces the developer to have a single button on the right of the UINavigationBar (ActionBar equivalent on iOS)1. Saving as much space as possible on iOS may be a good practice, it is not a big issue on Android. On Android, the system will automatically adapt the ActionBar appearance to ensure the maximum amount of actions is visible. You often have enough room to add two or three actions (regardless of the device you are running on) which is enough to maintain the main features on screen. For instance, in the Twitter application, it would have been possible to have a “Refresh” button next to the “New tweet” button.

  • The pull-to-refresh pattern is not easily visible to the user. The Android UI philosophy is to make the UI as clean and sober as possible. Unfortunately developers have a tendency to misunderstand this UI rule. They often over-engineer the UI by putting buttons everywhere, making their applications look like a cockpit! The actual rule is to make the UI as clear as possible by prioritizing and ensuring it remains accessible. Keep in mind that a mobile application is not intended to give the same level of functionality as its desktop/web equivalent. A mobile app has to be concise and has to provide an extremely well designed small subset of all of the product’s features. On iOS, people may see “pull-to-refresh” quite easily when the screen is displayed or when they are rapidly flinging the UITableView to its top. In that case, the list will bounce when reaching the top edge, letting the user see the “pull-to-refresh” widget. As far as I know, there is no equivalent on Android as all implementations are only visible from the very top of the ListView.

  • You have to be at the top of the ListView to refresh it. This may be easy on iOS as a click on the status bar automatically scrolls all UIScrollViews on screen to their top. Unfortunately this is not a default behavior on Android. As a result, a user who has reached the bottom of a ListView will have to scroll all the way up to the first item and then ask for a ‘Refresh’. Pretty boring, isn’t it?

  • It is not compliant with how Android represents scrollable contents. To my mind, this is the biggest issue when using “pull-to-refresh” in Android apps. On iOS, most scrollable containers inherit from UIScrollView which nicely ‘bounces’ when at least one of the edges is reached. As a result, creating a UI widget on top of a UIScrollView will let you benefit from the amazingly great bouncing mechanic that Apple implemented. It improves consistency and enforces its adoption by the users. Because of this, enhancing the ‘edge bouncing’ property with a “pull-to-refresh” widget is pretty natural to iOS users. Android users are not familiar with bouncing scrollable containers2. I am not saying Google didn’t make a mistake when they released the first version of Android not including such a nice behavior. It’s probably because the early Android devices had screens with lots of afterglow: having a bouncing animation would have resulted in having a blurry screen while the ListView would be springing back to its final position. I’m not happy with this either but I consider we now have to deal with it and stop misleading the user by changing how scrollable containers scroll at every new release. Google recently introduced the edge effect (see EdgeEffect for more information) so we have to stick to it. The pull-to-refresh is clearly not compatible with the edge effect (see screenshot below).

  • Most of the time the ‘pull-to-refresh’ is not necessary: I have seen many applications using the ‘pull-to-refresh’ pattern for items having a low “update rate”. As a consequence, implementing the “pull-to-refresh” pattern in those cases is almost useless. There is a little chance the list will need to be updated while the user is looking at it. Android has several mechanisms and callbacks you may use to refresh the contents when appropriate. Always prefer considering a smart and automatic refreshing mechanism. There is nothing better than simplifying the UI by anticipating what the user wants and doing it for him. For instance, you can look for new contents at every onResume calls, every X minutes, etc. You may also use components such as Service to refresh your contents in the background at fixed intervals of time (that has no equivalent on iOS) or the Cloud 2 Device Messaging (C2DM) service to push the new contents only when strictly necessary.

  • When visible, the “pull-to-refresh” widget is pretty intrusive compared to a regular “Refresh” button. The best example of this is probably the latest GMail application. When tapping on the “Refresh” button, it turns into an indeterminate ProgressBar while refreshing. You can continue reading your contents while still knowing whether or not the refresh is in progress.

Conclusion

I know some of you totally disagree with what I said in this article. As a huge fan of the ‘pull-to-refresh’ UI pattern on iOS I can’t blame you. I just wanted to show you that stupidly porting UI widgets from one platform to another is never a good practice. Please always consider platform differences when developing an application on different OSes. If you are like me, a developer using an Android device a daily basis and surrounded by iOS users (designer, boss, etc.) then you will have to fight everyday to explain that Android is different. It may sound weird to say that to people who “think different” but I actually do it everyday. It’s an ongoing fight. When I develop an application on several platforms I always ensure they all have the same set of functionalities but I always redesign the UI where necessary. That’s the price to pay to get an Android app on Android, an iOS app on iOS, etc. and have non-disoriented users.


  1. Technically speaking you can add several buttons to a UINavigationBar. For instance you may add a button on the left but this area is often reserved by the “back” button. You may also add a control in the center of the UINavigationBar but you will lose the UINavigationBar’s title. If you really want to add several buttons on the right of your UINavigationBar, you will often end up with tricks such as using a custom view (a UISegmentedControl for instance) in your UIBarButtonItem.

  2. I know some of you may mention some manufacturers implementations. Personally, I love how the UIScrollView (and of course its UITableView descendant) bounces on iOS but most of the implementations I have seen on Android so far are not great. They either don’t feel natural or are not consistent with the rest of the system. For instance, Samsung implemented it, and the movement is quite OK. But is not consistent with the rest of the framework: neither View (which has a basic scrolling support) nor ScrollView (even worse!) bounce.