A SECTIONED GRID FOR ANDROID


Tablets give you a lot of real estate to play around with. Google has created several features specifically to help developers take advantage of the extra screen space, most obviously the Fragment system. However, one of the most widely-used Android components, the ListView, has not been optimized for the wider screens you encounter on a tablet.

Here at Velos, we’ve often found that presenting information in a grid format can look really sharp on tablet devices. You can present many grid items in a single row, which lets the user see a lot of information before they need to start scrolling. Since grids with fewer columns present nicely on handsets as well, it’s also a good way to maintain consistency between handset and tablet while still taking advantage of each device’s strengths.

There is a pretty significant limitation with the standard Android GridView, though: it cannot be sectioned. Like with the ListView, there’s no API-level ability to add sections that divide up the items within your collection. Designers still enjoy specifying them, though, and for good reason: section headers help give users crucial context and help them more easily navigate through a large body of information.

We had previously tackled the section header limitation for basic scrolling lists, but what about grids? Imagine that we want to create an app that presents the books in a user’s library. To make it easier to browse, we’ll put each author in a separate section. That way, you can quickly scroll down to find your author, and then look through the rows to find your particular book. (It’s tricky to find an author who’s buried between two other, more prolific writers.) The app might look something like this on a tablet:

Sectioned grid displayed on a tablet.

A phone could use a list, or just fewer columns, like so:

Sectioned grid displayed on a phone.

This isn’t supported with any native Android UI components (at least, not to the best of my knowledge), but proved to be relatively simple to implement. The key is to forget about overriding GridView, and instead build this as a complex ListView. From the ListView’s perspective, each row is a single item; we can then enhance the Adapter subclass to manage the logic of translating to the specific item in each row.

When you take this approach, your Activity and ListView are very simple. All the grunt work needed to define sections, determine how many items fit within each section, where to display the headers, and so on, can be handled within the Adapter subclass.

We have created a reusable component that does exactly this, called SectionableAdapter. A SectionableAdapter overrides two important methods. getCount() handles the conversion between the actual number of items to display and the number of rows required to display them. For example, in the first screenshot above, the screen is displaying 17 items, split across 3 different sections, for a total of 4 rows. To do this, we keep track of how many cells we can fit in each row.

@Override
public int getCount()
{
    int totalCount = 0;
    for (int i = 0; i < sectionsCount; ++i)     {         int count = getCountInSection(i);         if (count > 0)
            totalCount += (getCountInSection(i)-1) / colCount + 1;
    }
    if (totalCount == 0)
        totalCount = 1;
    return totalCount;
}

Secondly, getView() similarly manages the translation between the list row, and the items within that row. From Android’s perspective, “David Mitchell” is a single list item; internally, we expand the 5 data items to draw within that row.

You can subclass SectionableAdapter and set it on your ListView to create your own sectioned grid. You just need to provide a few pieces of information.

  1. Create an XML layout file for each row within your grid. This will probably include an optional section header at the top, and one or more cell items below. You’ll likely want to create at least two versions of this file, one for handset and one for tablet, and you may even want to create different tablet versions to optimize for both 7″ and 10″ screens, or for tablets in portrait orientation.
  2. Tell the adapter the resource IDs you have defined for the header and to hold your cell items. SectionableAdapter will then manage all the bookkeeping around this: determining whether the header should be made visible, counting the number of columns in your grid, etc.
  3. Implement the abstract methods that tell the adapter how many sections you want to support, how many items are in each section, and what header to use for each section. These are completely under your control, and depending on how you source your Adapter data source they may be statically defined, loaded from the network, driven by user input, etc.
  4. Finally, implement the bindView() method. This is basically equivalent to the standard getView() method, except the adapter has already taken care of the boilerplate tasks like recycling views, inflating new views if necessary, and so on. All you need to do now is populate your item’s UI, and attach any click handlers or other controls you want to support.

Also of note: if you still want sectioned data, but want to present it in a list instead of a grid, that is supported by SectionableAdapter as a degenerate case where you provide a single column. This might be a useful approach if you are using fragments and don’t have much horizontal space on a tablet.

This class has served us very well so far. If you’d like to give it a whirl yourself, or check out the full code, head on over to our GitHub repository.

1 Comment