Handling Item Clicks in a ListView
Handling Item Clicks in a ListView is a common Android task when an app needs to respond to a user selecting a row. A tap might open a details screen, update a selected item, start a download, or display an alert. The basic pattern is simple: connect a listener to the ListView, identify the selected position, and use the related data safely.
A ListView displays information through an adapter, such as ArrayAdapter or a custom adapter. The listener receives the row’s position and the row’s stable ID, allowing the application to determine exactly which item was pressed. This approach works well for beginner projects, including a café menu for Melbourne customers or a local event list for Brisbane users.
The examples below use Java because it remains common in established Android tutorials and existing applications. The same event-handling ideas apply to Kotlin. Good click handling also considers accessibility, changing data, and screen navigation rather than treating the row position as permanent.
Connect the Listener to the ListView
Begin with a layout containing a ListView. The activity can populate it with an array of strings and attach an ArrayAdapter. Once the adapter is set, register an OnItemClickListener:
ListView listView = findViewById(R.id.listView);
String[] products = {
"Flat white",
"Sausage roll",
"Lamington",
"Iced coffee"
};
ArrayAdapter<String> adapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
products
);
listView.setAdapter(adapter);
listView.setOnItemClickListener((parent, view, position, id) -> {
String selectedProduct = products[position];
Toast.makeText(
MainActivity.this,
"Selected: " + selectedProduct,
Toast.LENGTH_SHORT
).show();
});
The callback provides four useful values. parent refers to the containing ListView, view is the tapped row, position is the row index, and id is the item ID supplied by the adapter. For a straightforward static array, products[position] is sufficient. The result could represent a Sydney café order, a Perth stock item, or a list of public holiday activities.
Understand Position and Item Data
The position argument starts at zero, so the first row has position 0, the second has position 1, and so forth. This matters when retrieving an object from an ArrayList. The selected item should be read at the moment of the click instead of being stored in a separate variable that may become outdated.
ArrayList<String> suburbs = new ArrayList<>();
suburbs.add("Newtown");
suburbs.add("Fitzroy");
suburbs.add("South Bank");
ArrayAdapter<String> suburbAdapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
suburbs
);
listView.setAdapter(suburbAdapter);
listView.setOnItemClickListener((parent, rowView, position, id) -> {
String suburb = suburbs.get(position);
openSuburbDetails(suburb);
});
When a list can be sorted, filtered, or updated, position alone may no longer identify a permanent record. A custom model class should contain a unique database ID, such as a SQLite primary key. Read that ID from the selected object and pass it to the next screen. This prevents a reordered list from opening the wrong item.
Open Another Activity from a Row
A frequent use for a row click is navigation to a details activity. Put the selected value into an Intent using an extra, then retrieve it in the destination activity. The same pattern can display details about an Australian train service, a market product, or a weekend event in Adelaide.
listView.setOnItemClickListener((parent, view, position, id) -> {
Product selected = productList.get(position);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("PRODUCT_ID", selected.getId());
intent.putExtra("PRODUCT_NAME", selected.getName());
startActivity(intent);
});
In DetailActivity, read the values during onCreate:
Intent intent = getIntent();
long productId = intent.getLongExtra("PRODUCT_ID", -1);
String productName = intent.getStringExtra("PRODUCT_NAME");
TextView title = findViewById(R.id.titleTextView);
title.setText(productName);
A unique ID is especially useful when records come from SQLite or a remote service. If a user browses a list of food stalls at a Sydney market, the detail screen can load the latest price and availability by ID instead of relying on text copied from the visible row.
Use Custom Rows and Safe Click Logic
Real applications usually need more than a single line of text. A custom row can contain an image, title, subtitle, price, and a favourite button. In that situation, use a custom adapter and bind the click listener to the ListView or to a specific child view, depending on the desired behaviour.
For a row-level click, the adapter can expose model objects while the activity handles navigation:
listView.setOnItemClickListener((parent, rowView, position, id) -> {
StoreItem item = (StoreItem) parent.getItemAtPosition(position);
if (item == null) {
return;
}
Intent intent = new Intent(this, ItemActivity.class);
intent.putExtra("ITEM_ID", item.getId());
startActivity(intent);
});
Using parent.getItemAtPosition(position) can be safer than reading from a separate collection when the adapter applies filtering. A row should also provide a clear visual response, such as a pressed state. Keep buttons inside the row focused on their own actions, so tapping a favourite icon does not unexpectedly open the full details page.
| Requirement | Suitable approach | Typical result |
|---|---|---|
| Static text list | ArrayAdapter<String> |
Show a toast or message |
| Database records | Custom model and adapter | Open details using a primary key |
| Filtered results | Read from the adapter | Use the item currently displayed |
| Several row controls | Child-view listeners | Separate favourite, delete, and open actions |
| Screen navigation | Intent extras |
Send an ID to a details activity |
Handle Updates, Accessibility, and Modern Alternatives
If list data changes after a click, update the underlying collection and call notifyDataSetChanged() on the adapter. Avoid keeping an old position for later use because insertions and deletions can shift every row. A deleted item should be removed by its ID or object reference, followed by a refresh.
For interactive rows, give text and images meaningful content descriptions where appropriate, maintain readable contrast, and ensure the tap target is large enough for comfortable use. Australian users may browse on small phones while commuting in Melbourne or waiting for a bus in Canberra, so the interface should remain usable in bright outdoor conditions and with one-handed operation.
ListView is still practical for simple screens and older projects, but RecyclerView is generally preferred for larger or more dynamic collections. When an app combines scrolling images with selectable content, related examples such as image slideshow tutorials can help structure the visual portion separately from the click logic. The same principle applies: keep display code, item data, and user actions clearly separated.
A reliable click handler identifies the current adapter item, validates its data, and performs one clear action. With that structure, a list can grow from a small demonstration into a dependable catalogue, menu, booking screen, or local services directory without confusing row selections.