Defining the Text in a JavaFX ComboBox with Objects
Do you know the problem that you have done a specific task already many times in the past, but it’s too long ago to remember exactly how you did it the previous times? One of those cases for me, is how you create a JavaFX ComboBox and configure it to show a specific field of an object in the opened and closed state of the ComboBox.
So to fix the problem, I documented it here in a blog post, and hope I remember that I blogged about it, the next time I need this functionality ;-)
The JavaFX ComboBox control is very useful for selecting a value from a list of options. This works very well with a list of strings, but by default, it doesn’t know how to properly display custom objects. In the first image below, you can see that the toString()
method of the object gets used.
Quick Solution: StringConverter
By using a StringConverter
for comboBox.setConverter()
, we only need a few lines of code to display the text of our choice in both the closed and open state of the ComboBox.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import java.util.Comparator;
public class ComboBoxWithObjectSimple extends Application {
@Override
public void start(Stage stage) throws Exception {
// Creating the ComboBox
ComboBox<Person> comboBox = new ComboBox<>();
// Add data to the ComboBox
comboBox.getItems().addAll(
new Person(1, "John", "Doe"),
new Person(2, "Bob", "Something"),
new Person(3, "Jane", "Doe"),
new Person(4, "Jill", "Hello")
);
// Define how to display the objects
comboBox.setConverter(new StringConverter<>() {
@Override
public String toString(Person person) {
return person != null ? person.firstName() + " " + person.lastName() : "";
}
@Override
public Person fromString(String string) {
// We don't need to handle this for display purposes
return null;
}
});
// Handle change selection
comboBox.setOnAction(event -> {
Person selectedPerson = comboBox.getValue();
if (selectedPerson != null) {
System.out.println("Selected person: " + selectedPerson.firstName() + " " + selectedPerson.lastName());
}
});
// Optionally, sort the items based on the last name
comboBox.getItems().sort(Comparator.comparing(Person::lastName).thenComparing(Person::firstName));
// Optionally, select the first item in the list
// Use with care, because setOnAction is not triggered, so you can't select the first item
comboBox.getSelectionModel().select(0);
var holder = new VBox(comboBox);
holder.setPadding(new Insets(10));
holder.setAlignment(Pos.TOP_CENTER);
var scene = new Scene(holder, 250, 200);
stage.setScene(scene);
stage.setTitle("JavaFX ComboBox Demo");
stage.setX(25);
stage.setY(25);
stage.show();
}
// The Java object used in the ComboBox
public record Person(int id, String firstName, String lastName) {
}
}
With this approach, both the selected item and the dropdown list items will display the full name of the person.
The source file ComboBoxWithObjectSimple.java
is available here.
Advanced Solution: Different Visualization for Closed and Open States
If different information must be shown in the opened state of the ComboBox, we need to add a comboBox.setCellFactory()
to specify the content to be shown in the list. In this example, I extended the persons with a color to illustrate you can do more UI styling in the opened view than only defining the text.
public record Person(int id, String firstName, String lastName, Color color) {
}
...
comboBox.getItems().addAll(
new Person(1, "John", "Doe", Color.RED),
new Person(2, "Bob", "Something", Color.LIGHTBLUE),
new Person(3, "Jane", "Doe", Color.LIGHTGREEN),
new Person(4, "Jill", "Hello", Color.LIGHTGREEN)
);
...
// Define the cell factory for the OPEN state (dropdown items)
comboBox.setCellFactory(p -> new ListCell<>() {
@Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty || person == null) {
setText(null);
} else {
// Format with both name and ID for dropdown items
setText(person.firstName() + " " + person.lastName() + " (ID: " + person.id() + ")");
// Add background color
setBackground(new Background(new BackgroundFill(
person.color(),
CornerRadii.EMPTY,
Insets.EMPTY
)));
}
}
});
The full source file ComboBoxWithObjectAdvanced.java
is available here.
Conclusion
- Only use a
StringConverter
to show the some in both closed and open states. - Combine a
StringConverter
for the closed state with a customCellFactory
for the opened state.
This same approach can also be used with other JavaFX controls that display items in a list, including ListView and TableView, to display complex data objects in a clear, readable way without the need of complex code.