Working with ComboBox
ComboBox is a JavaFX control that provides a dropdown list of selectable items. It's perfect for situations where you want to present the user with a fixed set of choices. In the Jukebox project, ComboBox is used in the Admin Panel to select the genre of an album. This cookbook section explains how ComboBox is implemented and used.
- Defining ComboBox in FXML
- Populating ComboBox with enum values
- Setting default selection
- Getting the selected value
- Using value property listeners
- Two-way binding with model objects
ComboBox in FXML
In the Admin Panel view, the ComboBox is defined in FXML:
<!-- In admin-panel-view.fxml -->
<ComboBox fx:id="genreComboBox" prefWidth="150" promptText="Genre" />
fx:id- Identifies the ComboBox for injection into the controllerprefWidth- Preferred width of the dropdownpromptText- Text shown when no item is selected
Setting Up ComboBox with Enum Values
In the AdminPanelController, the ComboBox is populated with Genre enum values:
private void setupGenreComboBox() {
genreComboBox.setItems(FXCollections.observableArrayList(Genre.values()));
genreComboBox.getSelectionModel().select(Genre.ROCK);
}
Genre.values() returns an array of all values in the Genre enum: [ROCK, HIP_HOP, COUNTRY, UNKNOWN]
FXCollections.observableArrayList(Genre.values()) creates an ObservableList from the enum values array. This allows the ComboBox to automatically update if the list of choices changes.
genreComboBox.setItems() sets the list of available choices in the ComboBox.
genreComboBox.getSelectionModel().select(Genre.ROCK) selects ROCK as the default genre.
- Type Safety: The ComboBox can only contain valid Genre values
- Fixed Choices: Genre has a fixed set of values that don't change
- No Magic Strings: Using enums avoids string literals like "ROCK", "HIP_HOP", etc.
- Easy Population:
Genre.values()gives you all values automatically
The Genre Enum
The Genre enum is defined in its own file:
package jukebox.model;
public enum Genre {
ROCK,
HIP_HOP,
COUNTRY,
UNKNOWN
}
When you populate a ComboBox with enum values, JavaFX automatically uses the enum's toString() method to display each value. By default, this shows the enum constant name (ROCK, HIP_HOP, etc.).
You can customize the display by:
- Overriding
toString()in the enum - Using a custom cell factory
- Using a custom string converter
ComboBox Value Property
Similar to TextField's textProperty(), ComboBox has a valueProperty() that represents the currently selected value. In AdminPanelController, a listener is added to update the album's genre when the selection changes:
genreComboBox.valueProperty().addListener((obs, oldVal, newVal) -> {
if (selectedAlbum != null && newVal != null) {
selectedAlbum.genre = newVal;
albumsList.refresh();
}
});
- User selects a value from the ComboBox dropdown
- ComboBox's
valueProperty()fires a change event - Listener receives the new value (a Genre enum constant)
- Listener updates the selected album's genre field
- Listener calls
refresh()to update the albums ListView
Getting and Setting the Selected Value
You can programmatically get and set the ComboBox selection:
// Getting the selected value
Genre selectedGenre = genreComboBox.getSelectionModel().getSelectedItem();
// Setting the selected value
genreComboBox.getSelectionModel().select(Genre.HIP_HOP);
// Clearing the selection
genreComboBox.getSelectionModel().clearSelection();
getSelectedItem()- Returns the currently selected valuegetSelectedIndex()- Returns the index of the currently selected valueselect(T value)- Selects a specific valueselect(int index)- Selects by indexclearSelection()- Clears the current selectionselectFirst()- Selects the first itemselectLast()- Selects the last item
Selection Listeners for ComboBox
ComboBox also has a selected item property that you can listen to:
genreComboBox.getSelectionModel().selectedItemProperty().addListener(
(obs, oldVal, newVal) -> {
if (newVal != null) {
System.out.println("Selected genre: " + newVal);
}
}
);
| Property | Type | Description |
|---|---|---|
valueProperty() |
ObjectProperty<T> |
The currently selected value (can be null) |
selectedItemProperty() |
ReadOnlyObjectProperty<T> |
The currently selected item from the items list |
In most cases, they behave the same, but valueProperty() is read-write while selectedItemProperty() is read-only.
Two-Way Binding with ComboBox
In the Admin Panel, there's a two-way binding between the ComboBox and the selected album's genre:
// 1. When an album is selected, the ComboBox is updated
albumsList.getSelectionModel().selectedItemProperty().addListener(
(obs, oldSelection, newSelection) -> {
if (newSelection != null) {
genreComboBox.getSelectionModel().select(newSelection.genre);
}
}
);
// 2. When the ComboBox value changes, the album is updated
genreComboBox.valueProperty().addListener((obs, oldVal, newVal) -> {
if (selectedAlbum != null && newVal != null) {
selectedAlbum.genre = newVal;
albumsList.refresh();
}
});
- User selects album: ComboBox is updated to show the album's genre
- User changes ComboBox: Album's genre is updated
- Album is updated: ListView is refreshed to show the change
ComboBox with Other Data Types
While the Jukebox project uses an enum with ComboBox, you can use any type of data:
// ComboBox with Strings
ComboBox<String> colorComboBox = new ComboBox<>();
colorComboBox.getItems().addAll("Red", "Green", "Blue");
colorComboBox.getSelectionModel().select("Red");
// ComboBox with custom objects
ComboBox<Person> personComboBox = new ComboBox<>();
personComboBox.getItems().addAll(person1, person2, person3);
personComboBox.getSelectionModel().select(person1);
// ComboBox with integers
ComboBox<Integer> numberComboBox = new ComboBox<>();
numberComboBox.getItems().addAll(1, 2, 3, 4, 5);
numberComboBox.getSelectionModel().select(1);
When using custom objects in a ComboBox:
- Make sure your object has a good
toString()method - The ComboBox will display each object using its
toString()method - Consider overriding
equals()andhashCode()for proper comparison
Customizing ComboBox Appearance
You can customize how items appear in the ComboBox using a cell factory:
genreComboBox.setCellFactory(lv -> new ListCell<Genre>() {
@Override
protected void updateItem(Genre genre, boolean empty) {
super.updateItem(genre, empty);
if (empty || genre == null) {
setText(null);
} else {
// Custom display format
setText(genre.toString().replace("_", " ").toLowerCase());
// Would display "hip hop" instead of "HIP_HOP"
}
}
});
- Custom formatting of values
- Adding colors or icons
- Displaying additional information
- Custom styling
ComboBox in Action: The Admin Panel Workflow
Let's trace through how ComboBox is used in the Admin Panel:
When the Admin Panel view is displayed, AdminPanelController.initialize() is called, which calls setupGenreComboBox().
The ComboBox is populated with all Genre enum values and ROCK is selected by default.
When the user selects an album from the albums ListView, the selection listener updates the ComboBox to show that album's genre.
When the user selects a different genre from the ComboBox, the value property listener updates the album's genre field and refreshes the ListView.
When the user adds a new album, the album is created with Genre.UNKNOWN by default, and the ComboBox updates to show this.
Creating Your Own ComboBox
Let's create a ComboBox for selecting from a list of options:
Step 1: Add ComboBox to FXML
<ComboBox fx:id="priorityComboBox" prefWidth="150" promptText="Priority" />
Step 2: Create Enum or Data
public enum Priority {
LOW,
MEDIUM,
HIGH
}
Step 3: Set Up ComboBox in Controller
@FXML
private ComboBox<Priority> priorityComboBox;
@FXML
private void initialize() {
// Populate with enum values
priorityComboBox.setItems(
FXCollections.observableArrayList(Priority.values())
);
// Set default selection
priorityComboBox.getSelectionModel().select(Priority.MEDIUM);
// Add listener for changes
priorityComboBox.valueProperty().addListener(
(obs, oldVal, newVal) -> {
if (newVal != null) {
System.out.println("Selected priority: " + newVal);
}
}
);
}
Try This:
- Run the Jukebox application and go to Admin Panel
- Add a new album and notice the genre ComboBox defaults to UNKNOWN
- Select the album and try changing the genre using the ComboBox
- Notice how the album's genre updates in the ListView (if you look carefully)
- Open
admin-panel-view.fxmland find the genre ComboBox - Open
AdminPanelController.javaand find thesetupGenreComboBox()method - Find the value property listener for the genre ComboBox
- Open
Genre.javaand see the enum definition
Common Pitfalls
If you don't set a default selection, the ComboBox will be empty. Always set a default or use promptText to indicate what the user should do.
Always check for null values in your listeners. The ComboBox value can be null if the user clears the selection.
If you update the underlying data (like the selected album), make sure to update the ComboBox to reflect the change. Otherwise, it might show stale data.
By default, enum values are displayed as their constant names (ROCK, HIP_HOP). If you want different display text, you need to override toString() or use a custom cell factory.
If you modify the list of items in the ComboBox after it's been set, make sure to use an ObservableList so the ComboBox updates automatically.
Key Takeaways
- ComboBox provides a dropdown list of selectable items
- Populate with
setItems()using an ObservableList - Use enum values for fixed sets of choices (like Genre)
- Set default selection with
getSelectionModel().select() - Use
valueProperty()to listen for selection changes - ComboBox uses
toString()to display items by default - Two-way binding can be set up between ComboBox and model objects
- Custom cell factories allow custom display formatting