How to get sticky / Pinned Headers in an ExpandableListView?

joecan picture joecan · Jun 6, 2011 · Viewed 8.8k times · Source

Has anyone had any luck adapting PinnedHeaderListView so that it can be used with an ExpandableListView instead of just a simple ListView with indexed sections? I basically want an ExpandableListView where each group item view stays pinned to the top until it is pushed up by the next group view.

I've studied the code to try and figure out how PinnedHeaderListView works and it seems like it will be difficult to adapt to an ExpandableListView. The main problem seems to be in the use of a different kind of adapter and drawing methodology. A PinnedHeaderListView makes use of SectionIndexer to keep track of section positions. As it draws each item with getView() it checks if the item is the beginning of a new section. If the item is the beginning of a new section it makes a section header visible within the item's list_item view. An ExpandableListAdapter has a getChildView() and a getGroupView() to draw the items and sections separately as different list items.

I am sure there must be some way of using the methodology in PinnedHeaderListView to get similar behavior in an ExpandableListView, but I am not sure where to begin.

Answer

joecan picture joecan · Jun 8, 2011

I was able to get pinned headers working on the ExpandableList1 APIdemo.

The hardest problem I faced was figuring out how to get the SectionIndexer to play nicely with expanding lists. As was evidenced in my question, I was thinking of this all wrong. In my original attempt to solve this problem, I created a SectionIndexer object within MyExpandableAdapter and mapped it to my data, similar to how it is done in the Contacts app and Peter's example. This works in the Contacts app because the flat list positions statically match the data set. In an expandable list view the flat list positions change as groups are expanded in and out.

So, the solution is to not to map the section indexer to the data, but to the instance of your ExpandableListView. With this solution, you don't even need a SectionIndexer object as used in the examples. You just need to wrap the SectionIndexer implementation methods around the ExpandableListView methods like this:

    @Override
    public int getPositionForSection(int section) {
        return mView.getFlatListPosition(ExpandableListView
                .getPackedPositionForGroup(section));
    }

    @Override
    public int getSectionForPosition(int position) {
        return ExpandableListView.getPackedPositionGroup(mView
                .getExpandableListPosition(position));
    }

There are of course other changes you have to make to get this all working, but the above methods are the key. I'll post the full code to google code or github and link from here soon. ExpandableLists with pinned headers look great!