Android’s event design sucks
While delving further into Android, I’ve been creating a “hello world” type of app – its not as complex as what MahTweetsMobile will be, but it is encompassing a lot of different elements (custom ListAdapter, SQLite for persistent storage, different types of menus, loading external intents, etc).
One “difficult” element has been events. I say “difficult” because by design, its freaking nuts.
Scenario
I’ve got a custom ListViewAdapter, for my custom list elements, where the ListView items include a checkbox. This checkbox is to indicate the status of the items (for reference, its a shopping list app, you tick the checkboxes to show which items you’ve bought while shopping, the items then go grey in colour).

I wanted to add a “long press” context menu so that you could easily bring up a menu (with options such as Edit Item or Delete Item)
The Problem
Normally when “attaching” event listeners with ListViews, you use setOnCreateContextMenuListener on the ListView itself. You then override your Activity’s onContextItemSelected, figure out what menu button was pressed as well as what item it was pressed on.
ListView x = (ListView)findViewById(R.id.ListView01);
x.setOnCreateContextMenuListener(new OnCreateContextMenuListener()
{
public void onCreateContextMenu(ContextMenu contextMenu, View view,
ContextMenuInfo arg2)
{
contextMenu.add(0,CMENU_DELETE, 0, "Delete Item");
}
});
public boolean onContextItemSelected(MenuItem item)
{
AdapterContextMenuInfo menuInfo =
(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
switch (item.getItemId())
{
case CMENU_DELETE:
//Use menuInfo.position along with the adapter.getItem
//ie Product p = (Product)adapter.getItem(menuInfo.position);
return true;
}
return true;
}
As you can see, its ugly code, but it works. Sort of. The problem was the checkboxes.
As soon as you add checkboxes to the individual items view, any listener created on the ListView never fires – the CheckBox swallows the event!
The Apparent Solution
Apparently this is a known bug (or more accurately, this is by design!). The solution is to set the event handlers on the rows themselves; inside the custom ListAdapter, when you’re inflating the various UI elements – look at my previous post on Custom ListAdapter, it’s under the getView method – on that “rowLayout” use the same setOnCreateContextMenuListener as the code used on ListView above, except only apply it to the individual row’s.
That would be fine if you didn’t care which item was pressed. That’s right, the cast to AdapterContextMenuInfo of item.getMenuInfo() in your Activity’s (yes, your activity has to override this, nowhere else works. argh!) onContextItemSelected always returns null because item.getMenuInfo() is always null.
The Actual Solution
I have no idea how I stumbled across this solution – I think I was in the process of undo-ing lines of code to try and figure out “where I’d gone wrong”.
Fact: To get the event to fire with checkboxes you need it to be set on the rowLayout.
Fact: To get menuInfo to be anything other than null (ie, tell you where it came from), you need it set on the ListView
Solution? Combine them. Wait, what? Two event listeners to get them to work? Yup.
On the rowLayout, set the listener:
rowLayout.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenuInfo arg2) { } });
Notice the lack of code inside the onCreateContextMenu? Well, any ContextMenu items added inside this method won’t have AdapterContextMenuInfo – so don’t set anything, no menus will appear.
Now also set the adapter on the ListView using the code above.
Now it all works. WTF doesn’t begin to describe this.
