A common issue when trying to respond to the tap of a list item is where onListItemClick is not called for a ListFragment, ListActivity, or ListView. The root cause is almost always one of two issues.
Assume that you have an app with a list that displays rows which contain a checkbox and several text fields. When you tap a row in the list you want to open a details screen for that list item and when you tap on the checkbox you want to do something interesting with whether that item is selected (what you actually do with the checkbox is not important for this discussion).
You will likely use a custom adapter to present items in the list and you will likely have a view hierarchy similar to the following.
Ideally, as you tap each row, an
onListItemClick implementation will be called so that you can then display details for that list item and the checkbox should do something interesting when its state is changed.
First possible issue
There is a chance your implementation might be similar to the following. Notice the bold
android:clickable attribute on the layout.
When handling touch events (ex: tapping on a list item), Android will start at the bottom of the touched view hierarchy and give each view a chance to respond to and consume the event before its parent1. If for example you touch
TextView2 will have a chance to respond to the touch event, followed by
Layout1, and so on all the way up the view hierarchy.
In this case,
Layout1 consumed the touch event because it had
android:clickable set to
true which caused a default no-op
onClickListener to be installed on that view. Because the touch event was consumed by that automatically installed onClickHandler, there was no touch event for the parent
ListView to handle.
The solution in this case is simple: remove the subtle and erroneous
android:clickable attribute and your
onListItemClick implementation will be called. I’m specifically picking on
android:clickable=true because it seems to be a common attribute to naively try as a “fix” when things don’t seem to be “clickable”. Paying close attention to what views in your view hierarchy are actually consuming the touch events is important.
Second possible issue
If you look at the view hierarchy above, notice that each row in our list contains a CheckBox. CheckBoxes (along with many other interaction views) are focusable by default and when inside ListViews will effectively steal your touch event. 2 This is by design; you can read the bug report here. To solve this issue, make the checkbox not focusable by setting
android:focusable="false" on the CheckBox in the layout. When you press on the row, your
onListItemClick callback will be fired (and the row background state will be displayed correctly) and the CheckBox behaves as one would expect.
It is important to note that this is not a perfect or complete solution because if someone is using a directional pad to navigate your app (ex: for accessibility reasons or on a TV remote), they will not be able to manipulate the checkboxes. 3 There is not a good way to deal with this problem so if accessibility is important to your app you should consider an alternative user interface design that does not include focusable child views in a ListView.
“You can have parent views handle and consume touch events before child views, but for simplicity we are ignoring that fact.” ↩
“Physical devices with a d-pad are sometimes hard to obtain and the AVD tool currently does not make it easy to create an emulator with an enabled d-pad. You can work around this shortcoming by editing
~/.android/avd/[your emulator name].avd/config.iniand changing the
yes. Note: The specific location where your emulator(s) exist may vary based on your configuration or operating system. ↩