CWAC'd Up: Alternative Adapters
The CWAC’d Up series covers the author’s CommonsWare Android Components, open source libraries you can use to enhance your Android applications.
Android’s ListView is excellent at creating lists where everything is the same. Even if rows might be rendered differently on a row-by-row basis, the underlying row data tends to all be the same: the results of a SQLite query and wrapped in a CursorAdapter, or objects you insert into an ArrayAdapter, etc.
However, you can clearly see other applications that appear to be using ListView where the contents vary. The application-details activity in the Android Market, for example, feels like a ListView, with a mix of selectable rows, headings, and other rows that are not selectable at all. While one can accomplish all of this with some fancy adapter footwork, or in some cases with addHeaderView(), it would be nice to be able to do this without so much work.
SackOfViewsAdapter does pretty much what it says it does: you give it a List of View objects, and it treats those as rows. You do not have to mess around with on-the-fly inflation, row binding, or anything you do not want — just set up the Views at the outset and go. Of course, this approach is not good if you want to have a lot of rows, or an arbitrary number of rows. But, for a small number of Views, performance is perfectly reasonable.
Most times, though, you will want to use MergeAdapter. MergeAdapter allows you to blend multiple ListAdapters and Views into a single ListView. You teach the MergeAdapter what ListAdapters and Views to display, in what order, and MergeAdapter handles blending all of their individual contents into a single roll-up Adapter interface presented to the ListView.
So, let’s think about the Android Market application detail screen:
After a fixed header that does not scroll, everything else in this activity scrolls. Moreover, since some rows (e.g., “My rating”, “Post a comment”) are selectable and others (e.g., headings, the application description) are not, one would assume this is implemented via a ListView.
To do this with SackOfViewsAdapter, you would create one View for each row in the list, put them in a List<View>, pass that List to the SackOfViewsAdapter constructor, and pop the resulting Adapter into your ListView. Android and SackOfViewsAdapter handles the rest.
But, suppose that the Android Market screen did not only show three comments, but a much longer list of comments. Pre-creating all of those Views as rows could be expensive. The more natural way to do that would be via some CursorAdapter. That’s where MergeAdapter comes in. Call addView() on a MergeAdapter for individual Views you want as rows, and where you want the extended list, call addAdapter() and provide your CursorAdapter. Then, you will get as many rows as your Cursor requires, wherever that list is supposed to appear in the overall ListView around the rest of your rows.
To handle enabled/disabled rows, simply extend SackOfViewsAdapter or MergeAdapter and override isEnabled() to return true or false as needed.
Both of these adapters are packaged as tight JAR files: no resources, no extraneous classes. You can therefore either integrate the Apache-licensed source into your own project tree, or build the JAR and add it to your project (e.g., drop it in libs/). Just use the ant jar task to build the JAR file. Each project does come with a demonstration activity, so the normal ant debug task will build that demo app’s full APK.
Next time in CWAC’d Up: Make your ListViews endless!