complications with ListView and headers/footers

Well, of course it’s always the obvious thing you don’t try. There really was only one field in R.drawables – what I wanted was android.R.drawables! *facepalm* GridView is much more useful here than Gallery – not sure where Gallery would actually be preferable now that I look at it.

I ran into a little quirk. I set up a listview’s data by calling getListView().setAdapter(adapter), and later when I tried to retrieve the adapter with getListAdapter(), I got null. I dug into the platform source a little bit. It seems that ListActivity#setListAdapter sets the adapter both on the ListView and its own member mAdapter. ListActivity#getListAdapter is not simply a convenience method replacement for getListView().getAdapter() but actually gets the stored mAdapter in the ListActivity. Apparently the intention is that ListActivity#setListAdapter be called so that both places are set. But the storage in two places isn’t necessarily redundant – when ListView#setAdapter is called, if headers have been added to the ListView, the adapter is wrapped as new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter) and stored. I saw some reference to this before but didn’t understand it until I saw it for myself.

Here’s another interesting bit I found in the code for ListView. If you add a header with addHeaderView(v), it is assumed you want that view to receive focus / be selectable, because that’s how it’s implemented (though docs say nothing):
    public void addHeaderView(View v) {
        addHeaderView(v, null, true); // where “true” is for “isSelectable”
    }

I’m not sure why this would be the default, but it is what I’ve observed – headers act like list items (and footers work the same way). I think it’s out of consideration for those who are using buttons or trackballs to navigate and so need to be able to “get to” something to see it. This isn’t so great if your header/footer has views in it that need to receive focus/clicks, like form fields. The entire header/footer gets focus, not the views.

But actually that’s true even if the header/footer isn’t selectable – then the focus doesn’t go beyond the list items. Either way there doesn’t seem to be a way to navigate to the subviews, unless of course you have a touchscreen and can just touch them. That’s fine with me but I’m not sure it’s great for everyone else. Maybe there’s a way to work with the attributes to manually set up where focus goes.

And it gets better – if the list has no items in it and an empty view defined, the empty view is shown, and NO header or footer! This seems dumb to me. Maybe I’m just dense but I think the most useful thing to do with a header or footer is implement a button or input form to create a new list item, right? So you want to see “list has no items, create a new one here” but the way it works now, you would have to re-implement your header/footer in the empty view – even if you can <include> the relevant layout, you would need to wire up observers and data for it separately.

So to recap what I’ve found so far: if you want a list with headers/footers that scroll with the list and include focusable elements themselves, ListView has significant problems because there’s no way to navigate from the list items to the header/footer items. And if you specify an empty view, the header/footer won’t display.

This is the 1.5 API – I wonder if any of this works differently in later APIs? This seems like a common problem, and it seems easily fixable in the platform – just have headers and footers display with the empty view, and have the ends of the list continue focus to the header or footer views if the header/footer isn’t selectable. But this must be harder than it sounds or surely the main Android developers would have done it already.

Maybe I can create a custom ListView subclass that can accept XML attributes specifying header/footer elements and also automatically wires up where the focus goes for them.

Advertisements

7 Responses

  1. I have the same gripes regarding focusable elements inside a header/footer item, and hiding the entire list (including header/footer) when using the emptyView.

    Would be nice to have some solid “best practice” example of how to implement both. So far I haven’t found anything that seems “best”.

    • i’ve been meaning to write a longer post with pros/cons of approaches i’ve tried so far. there doesn’t seem to be a great solution really, just least problematic for particular situations.

  2. Hi Luke. Have u been facing problems with listview’s listener when you have a addHeaderView(View v) to your list. I cant see why this method makes the list forget where it was returning from a listitem click.

  3. I’ve been struggling through the same issues the last few days. I’ve disabled my emptyView entirely and so I can see both the header and the footer. What I dislike the most about the ListView control is the complete inability to hide the header and footer views. I’d love to be able to hide them at particular points in my code. The best I’ve been able to do so far is set them invisible, but the space taken up in the list is still reserved. Suboptimal.

    • Neal, have you tried setting visibility to GONE? That at least theoretically should release the space an item takes.

  4. Hi, you can try listview.setItemsCanFocus(true) after you add the header listview.addHeaderView(button). It should work.

  5. Luke, Thanks for good conclusion.
    And fatcong, I tried your opinion. I’ve got great success.
    It’s important to call setItemsCanFocus(true) after addHeaderView().

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: