Exploring Android ListView

Some time ago I summarized some things I’d discovered about headers and footers on ListView in Android. Oddly enough, despite the dearth of real content there, it is consistently one of the top-viewed pages on this rather esoteric and scatter-brained blog. I think the simple reason is this: ListView is frustratingly obtuse if you try to do anything that’s not explicitly described in the docs, and because everyone needs to use Listview to display lists of information, everyone is looking for solutions to fairly common usage problems.

Recently I’ve had reason to revisit ListView’s oddities with a vengeance. I created some example code I could play with. I think this may be useful to others, so I created a github repo to share it – it will evolve as I try things out / figure things out. Let me know if it’s useful to you or if you have any better ideas for how to accomplish what I’m attempting.

Static footers

This is actually very simple. It might even match the API docs somewhat. If you want to have a ListView with a footer that stays at the bottom of the screen (doesn’t scroll with the list items), simply create a vertical LinearLayout with the ListView and footer as the two items. The android:layout_height on the ListView and footer should be fill_parent and wrap_content respectively, as you would expect, but the magic is to add android:layout_weight=”1″ on the ListView (and its empty view if you have one). Without it, the ListView really does fill_parent and if there’s no room for the footer when it’s done, too bad. But with the weight specified, the ListView’s layout actually only fills the space not consumed by other wrap_content elements (or contested by other fill_parent elements). So your layout should be something like this:

<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
>
 <ListView
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_weight="1"
  android:id="@android:id/list" />
 <TextView
  android:id="@android:id/empty"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_weight="1"
  android:text="This is the empty element (optional)" />
 <TextView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="Static footer"
 />
</LinearLayout>

I could not get this to work with the 1.5 SDK and the RelativeLayout as others seemed to have success with. (Other discussions: 1 2 3.) I chalk this up to 1.5 layout bugs and simply use a LinearLayout – not much reason to do differently, so just move on.

Scrolling header/footer context menus

What made me want to embark on this project, aside from previous problems I had with ListView, is a problem I ran up against in WhenDidI: I decided to have some items in a ListView’s scrolling header that would require a context menu, and the ListView items required a context menu as well. What happened when I long-clicked on these items is that I got menu items from both the header and the list items; the latter being extraneous. By tinkering with events in my toy Listviews-explorer project, I could see what was actually being called when against what.

The first thing I learned, while actually reading the docs (I know, novel concept, but were they there when I looked before?) is that the onCreateContextMenu callback is actually supposed to be called every time a context menu is shown; this is unlike dialogs or the options menu – the result of the creation is not stored and reused. So if you want to customize your context menu to the item and the current environment, nothing is stopping you; you can do everything in the create method.

But for the solution to my problem – evidently the callback is called twice, once each with the footer element and the ListView as arguments, as each specifies a listener; and critically, always in that order (I’m guessing, more specific to less specific container). Just check in the callback if there are already menu items in the menu before adding more; then the ListView callback won’t add its items to the header’s menu.

Aside on long and short clicks

I wish I’d captured my project in git as it was earlier. I was seeing the listener report that a long click resulted both in a long click event and then a regular click event. I’m not seeing that anymore. Wonder what that was about.

Giving focus to children

One of the problems I also wanted to solve was the problem of having items within the scrolling headers/footers that can receive focus and be clicked on, like text editors or buttons. The default ListView setup is to ignore such elements while traversing the last (with a Dpad or trackball) and only let the list items (and thus headers/footers) get focus – never a button within an item. With a touch screen it doesn’t really matter so much (you just poke at what you need), but a proper implementation should do the right thing when navigating in any way.

In an old (and ultimately unsatisfying) thread Romain Guy said this couldn’t be done; but I’m hoping there are ways as of 1.5.

I tried one strategy for giving focus properly. Essentially it adds a listener to the item selection event, inspects what’s selected, and adjusts focus accordingly. This worked fine as long as there was only one item with such elements. It did not work quite right once I had both a header and footer with sub-elements; weirdly, when I navigated to the footer, the header sub-element became selected first, then another down-arrow would bring me finally to the footer sub-element. Another oddity is that the item (header/footer) seems to think it’s selected when really a sub-element is: the text color changes as if there were the usual orange selector background. I’m sure this could be made to work well for some situations.

Some other similar things I want to test out:

Android Row becomes Unclickable with Button

ListView and Buttons inside ListView

2 Responses

  1. Thank u so much , The static footer was very useful

Leave a comment