Issue
I am currently working on a game with Java and JavaFX. I am using a JavaFX ComboBox.
The following example should explain my problem.
Let's say I have a class "Animal" with the attributes "name", "age" and "color".
public class Animal {
private String name;
private int age;
private String color;
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
}
Now I want to create a ComboBox with each animal I create.
Second file:
ComboBox<Animal> comboBoxAnimal = new ComboBox();
ObservableList<Animal> comboBoxItems = FXCollections.observableArrayList();
Animal dog = new Animal("Liam", 2, "Brown");
Animal cat = new Animal("Emily", 5, "Gray");
Animal bird = new Animal("Kian", 3, "Green");
comboBoxItems.addAll(dog, cat, bird);
comboBoxAnimal.setItems(comboBoxItems);
Currently I get only "Animal@xxxxxxxx" which is understandable because I have a ComboBox of Animals but want only the names (Strings) to be presented.
Just simply creating a ComboBox<String>
won't solve the problem as I need a Combobox<Animal>
.
How can I get a Combobox<Animal>
but as elements show only the names of each Animal?
Thanks for your feedback :)
Solution
Three options (only the first two are recommended):
- Use a cell factory.
- Use a string converter.
- Override
toString()
in the backing item class (do not do this for this purpose).
You might also be tempted to try to put nodes directly in the ComboBox list (for example create Labels with the name of an object in each Label and then create a ComboBox<Label>
). Don't do this, as advised by the ComboBox API documentation section: "A warning about inserting Nodes into the ComboBox items list", it will create bugs in your application.
The cell factory and string converter examples used in this answer produce the identical output:
Cell Factory Implementation
Use a cell factory, like in this answer:
The linked answer is for a ListView, but the ComboBox is similar, as is a TableView or other virtualized controls that rely on cell factories for display.
To configure cells for the ComboBox drop-down list and button, make calls to both setCellFactory
and setButtonCell
.
This is the most flexible solution and allows for customization beyond just strings of text. Graphic nodes can be created to completely customize the visual representation of each combo box cell.
Example Code
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.stage.Stage;
public class AnimalComboApp extends Application {
public record Animal(String name, int age, String color) {}
public static class AnimalCell extends ListCell<Animal> {
@Override
public void updateItem(Animal animal, boolean empty) {
super.updateItem(animal, empty);
if (animal == null || empty) {
setText(null);
} else {
setText(animal.name());
}
}
}
@Override
public void start(Stage stage) throws Exception {
ComboBox<Animal> comboBox = new ComboBox<>();
comboBox.getItems().setAll(
new Animal("Liam", 2, "Brown"),
new Animal("Emily", 5, "Gray"),
new Animal("Kian", 3, "Green")
);
comboBox.setCellFactory(listView -> new AnimalCell());
comboBox.setButtonCell(new AnimalCell());
comboBox.getSelectionModel().select(0);
stage.setScene(new Scene(comboBox));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
StringConverter Implementation
An alternative to a cell factory definition is to provide the ComboBox with a StringConverter which can convert to and from a String and an object.
The StringConverter
requires fromString
and toString
to be implemented, but the fromString
implementation can just return null
unless you also want the user to be able to perform text edits to edit the combo box value.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class AnimalComboApp extends Application {
public record Animal(String name, int age, String color) {}
@Override
public void start(Stage stage) throws Exception {
ComboBox<Animal> comboBox = new ComboBox<>();
comboBox.getItems().setAll(
new Animal("Liam", 2, "Brown"),
new Animal("Emily", 5, "Gray"),
new Animal("Kian", 3, "Green")
);
comboBox.setConverter(new StringConverter<>() {
@Override
public String toString(Animal animal) {
return animal.name();
}
@Override
public Animal fromString(String string) {
return null;
}
});
comboBox.getSelectionModel().select(0);
Scene scene = new Scene(comboBox);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
toString() implementation
Do not override toString()
to customize the cells, that is an anti-pattern.
Use toString()
for other purposes such as listing and debugging all the elements of the Animal class, and instead, use the appropriate methods, such as cell factories or string converters, for customizing the UI view of the Animal
.
Answered By - jewelsea
Answer Checked By - Robin (JavaFixing Admin)