I came up with a generic and reusable solution. Instead of extending a concrete list adapter and modifying the getView()
method, I created a new class implementing the ListAdapter
interface that blindly forwards almost everything to another ListAdapter
except getView()
. It looks like this:
public class SubClickableListAdapter implements ListAdapter {
public static interface OnSubItemClickListener {
public void onSubItemClick(View subView, int position);
}
private ListAdapter other;
private SparseArray<OnSubItemClickListener> onClickListeners;
public SubClickableListAdapter(ListAdapter other) {
this.other = other;
onClickListeners = new SparseArray<OnSubItemClickListener>();
}
public void setOnClickListener(int id, OnSubItemClickListener listener) {
onClickListeners.put(id, listener);
}
public void removeOnClickListener(int id) {
onClickListeners.remove(id);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = other.getView(position, convertView, parent);
for(int i = 0; i < onClickListeners.size(); i++) {
View subView = view.findViewById(onClickListeners.keyAt(i));
if (subView != null) {
final OnSubItemClickListener listener = onClickListeners.valueAt(i);
if (listener != null) {
subView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onSubItemClick(v, position);
}
});
}
}
}
return view;
}
// other implemented methods
}
The other implemented methods simply look like the following one:
@Override
public Object getItem(int position) {
return other.getItem(position);
}
To use it, simply instantiate it supplying any other ListAdapter
(be it ArrayAdapter
or SimpleCursorAdapter
or anything else). Then call setOnClickListener()
for each view where you want to listen on the clicks, giving its id in the id
parameter, and your listener in the listener
parameter. To get the row id for the row which was clicked, call the getItemIdAtPosition(position)
method of your ListView (which you have to get some other way, because it's not given as parameter to your callback, but that shouldn't be a big problem in most cases).
The advantage of this solution is that it can be used with any ListAdapter
. So if your application has several ListView
s, each using different underlying views, or even different adapters, you don't have to create a new adapter class for each one.
The problem with this is the same as with all the other solutions: the OnItemClick()
of the ListView
won't be called if you click on a view that you registered a listener for. For views that you didn't register a listener, this callback will be called though. So, for example, you have an activity for your list item that contains two text fields and a button, and you register a listener for the button, then clicking on the button won't call the OnItemClick()
of the ListView
, but your callback instead. Clicking on anywhere else calls OnItemClick()
.