If there’s one piece of the core Android framework that every Android dev struggles with, it’s ListView. ListView is incredibly flexible and complex, and you’ll probably find you need it more than once in any decent-sized app. If you haven’t already slammed your keyboard and screamed at ListView before, you probably haven’t been writing Android apps very long. It’s so important, Google even had a whole session about it at their I/O conference in 2010.
ListView is the crucible, the teeth-cutting, the rite of passage for all aspiring Androidians. It’s like Luke seeing Darth Vader in the cave on Dagobah. Once you’ve battled with ListView and emerged from the cave victorious, you’ll know you’re a true Android developer.
This is just one story about ListView.
When I was writing Pokédroid, I came across an interesting problem. The first screen of the app was just a huge list of creatures, but it was too difficult to navigate through. Depending on what game you had, you were only interested in the ones numbered 1-151 (first generation), 152-251 (second gen), 252-386 (third gen), 387-493 (fourth gen), or 494-649 (fifth gen). This meant that the newer (and therefore more interesting) Pokémon were at the bottom, where they were hard to get at. But assuming the National Pokédex numbering, this was just the proper order.
Problem: there were too many goddamn Pokémon.

Too goddamn many.
The solution I came up with was to make the list more navigable by showing “fast scroll” overlays with the names of the various Pokémon generations. Named after the games’ regions, they go “Kanto,” “Johto,” “Hoenn,” etc. That way, the user could immediately know what section of the list they were in, and they could quickly scroll between sections.
Lots of Android apps do a similar thing. The Contacts and Music apps, for instance, show overlays to let you know which part of the alphabet you’re on:


This is made possible by the use of the “fast scroll thumb,” i.e the little grooved square to the right. It allows you to zoom through your list contents and hone in on the item you want. It’s like blasting down the highway and watching the exit signs, versus crawling down a suburban street, inspecting each house number one-by-one. It’s a much better user experience.
So the fast scroll thumb is awesome. And to use it, all you have to do is add fastScrollEnabled=”true” to your ListView’s XML. The only catch? If you want to use it for anything other than alphabetical sorting, your section overlays are going to look like this:

Bleccch.
Yup, the overlay has a fixed width, so you can only really use it for single characters. What’s a poor Android developer to do?
As it turns out, the only way to fix this problem is to implement your own version of the Contacts app’s internal FastScrollView and hack it yourself. I wasn’t the first to discover this, but I did post some snippets of the solution to Stack Overflow back when I first implemented it in Pokédroid. Since then, I’ve been getting some questions and clarification requests on the original post, so I decided to go ahead and write a full demo app to show how it works. After all, Pokédroid is and will probably always remain closed-source, but this code at least is probably worth sharing.
The demo app is on GitHub. Since Pokémon is kind of an esoteric subject, I decided to go with the topic of countries and continents instead. In this example, we’ve got a big list of countries, sorted either by continent or by country name. When you use continent-sorting, you can see overlays of the continents:
…and when you sort by the country name, you see alphabetic overlays instead:
Of course, if you wanted to get really fancy, you could vary the width of the overlay based on what kind of sorting you’re using. But it should be clear enough how to do that from the source code. In any case, with Pokédroid, I had a handful of different sorting mechanisms, but the most common ones had rather long titles, so I just kept the width the same for all of them. In the end, it looked like this:



That’s Pokémon sorted by generation, type, and base HP. The possibilities are pretty endless. You can take your ListView and sort it, divide it, slice-n-dice it however you want.
The important thing is that “fast scroll” sections make for a better user experience. ListViews can hold a lot of data, but that doesn’t mean you should let your list get bloated and then leave all the hard scrolling up to the user. I have an app on my phone where the developer uses an unsectioned ListView with over 200 items. Two hundred! It takes almost five seconds just to scroll from top to bottom! That may not sound like much, but in the UI world, five seconds is an eternity.
Just imagine your poor users, holding their phone in one hand and flipping your ListView with the other hand, over and over again, like they’re trying to light a wet match. Then reflect on how much you could improve that experience with some fast scroll sections.
Well, ListView-abusing Android developers (you know who you are): now you have no excuse. The CustomFastScrollView code is public and open-source, so go use it. Get cracking!
Posted by Lee on August 27, 2012 at 6:16 AM
Can you make it an endless scrolling list view so that it can pull a page from server and display it as needed using background AsyncTask? I certainly like your overlays but it seems a one time data loading. Thanks in advance.
Posted by Nolan Lawson on September 18, 2012 at 3:27 PM
Sure, there are tons of ways to do that. For instance you can use this guy’s solution: http://benjii.me/2010/08/endless-scrolling-listview-in-android/, and then call my CustomFastScrollView’s listItemsChanged() method in your AsyncTask’s onPostExecute() method, after the new data has been loaded. This will refresh the overlay sections.
Posted by Paul on January 17, 2013 at 5:24 PM
Thank you, this is an excellent implementation which is quite tedious to make in Android. There does appear to be a small bug though sometimes it crashes.
The bug appears when scrolling to the button of the list, and then and an IndexOutofError exception occurs when getPositionForSection is called. The index is always 1 larger than the total number of sections. So I think the code to calculate which section to return may be off by one but I’m not sure where to fix it.
I’m not sure why its only occurring for one of my data files. For all the others its working fine.
Posted by Nolan Lawson on January 18, 2013 at 10:23 AM
Hi there,
Could you please write up a detailed bug report on GitHub? A stacktrace would do wonders do help me look into this. :)
I’ve already copied and pasted what you’ve written here: https://github.com/nolanlawson/CustomFastScrollViewDemo/issues/1
Thanks!
Nolan
Posted by Paul on January 18, 2013 at 2:52 PM
Thanks I will post one. I actually used the code from the KeepScore app, so you may want to move the bug report over there. i.e. the SeparatedListAdapter