Let's start creating a class called Member. In this class we have two member variables. One is a String and is the name of the member the other is his age. Here the code:
public class Member { String name; int age; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
A typical JavaBean right? :). Now we declare our ComboBox using our own class.
@FXML private ComboBox<Member> comboBox;
The next step is to create a ObservableList which will hold a list of Persons. I will talk more about the ObservableList's later, for now I just use it and add some members into it.
ObservableList<Member> list = FXCollections.observableArrayList(); list.add(new Person("Cengiz")); list.add(new Person("Mueller")); list.add(new Person("Nick"));
How do we assign that list to the combobox? Easy, we use the ComboBox.setItems(...) method to do so. After doing that I like the ComboBox show me the first item in the list as choosen one.
comboBox.setItems(list); comboBox.getSelectionModel().selectFirst();
Here the whole code. Don't forget to prepare the FXML file with a ComboBox in it :). So here how it looks like.
package javafx.tutorials; import java.net.URL; import java.util.ResourceBundle; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.ComboBox; import javafx.stage.Stage; public class Tutorial3 extends Application implements Initializable{ public class Member { String name; int age; public Member(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } @FXML private ComboBox<Member> comboBox; @Override public void start(Stage stage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("Tutorial3.fxml")); loader.load(); Scene scene = new Scene((Parent) loader.getRoot()); stage.setScene(scene); stage.show(); } @Override public void initialize(URL location, ResourceBundle resources) { ObservableList<Member> list = FXCollections.observableArrayList(); list.add(new Member("Cengiz")); list.add(new Member("Mueller")); list.add(new Member("Nick")); comboBox.setItems(list); comboBox.getSelectionModel().selectFirst(); } public static void main(String[] args) { Application.launch(args); } }
Ok, now let's start the applicaton. And what do we seeeeee.....
Uuuuupsssss. What happend here? Of course, what we see in the combo box fields is text but how can the ComboBox object know what to use to represent the string over there? It just knows the "Member" object. Our solution is easy. The documentation of JavaFX tells us to use the ComboBox.setCellFactory(...) method. That helps us to define how we present the items in the ComboBox itself. OK, I thought, let's use that no problem. And here the additional part of the code ...
comboBox.setCellFactory(new Callback<ListView<Member>, ListCell<Member>>() { @Override public ListCell<Member> call(ListView<Member> param) { return new ListCell<Member>(){ @Override public void updateItem(Member item, boolean empty){ super.updateItem(item, empty); if(!empty) { setText(item.getName()); setGraphic(null); } } }; } });
Now let's start the code again.
Oh yeahh cooool it wooooorrkssssssssss .... eeeeee not? If I select one of those members I still have a wrong representation of my object. Now the elements in the list are correct but the label which shows the current selected one is still wrong? How do I change that?
I have to admit I was looking and looking and couldn't find anything in the World Wide Web. I checked the documentation ComboBox, which tells us something additionally important too. It's a warning about using nodes in the ComboBox itself. But that didn't solve my problem either. Nevertheless the solution at the end was so damn easy. I noticed that the String representation was using Java's Object's class names.
I just had to override the toString for Java objects and that's it. .... I had it.
And actually for the "Member" example I didn't have to make my own cell factory anyway. It's completely enough to implement the toString method that's it.
Of course if I want show some icons or colors as items in the ComboBox or other controll elements than I have to implement that function. But that I'll do in the next section. Here the complete code (I let the setCellFactory implementation in just to see how to do it anyway:
package javafx.tutorials; import java.net.URL; import java.util.ResourceBundle; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.ComboBox; import javafx.stage.Stage; public class Tutorial3 extends Application implements Initializable{ public class Member { String name; int age; public Member(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return name; } } @FXML private ComboBox<Member> comboBox; @Override public void start(Stage stage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("Tutorial3.fxml")); loader.load(); Scene scene = new Scene((Parent) loader.getRoot()); stage.setScene(scene); stage.show(); } @Override public void initialize(URL location, ResourceBundle resources) { ObservableList<Member> list = FXCollections.observableArrayList(); list.add(new Member("Cengiz")); list.add(new Member("Mueller")); list.add(new Member("Nick")); comboBox.setItems(list); comboBox.getSelectionModel().selectFirst(); comboBox.setCellFactory(new Callback<ListView<Member>, ListCell<Member>>() { @Override public ListCell<Member> call(ListView<Member> param) { return new ListCell<Member>(){ @Override public void updateItem(Member item, boolean empty){ super.updateItem(item, empty); if(!empty) { setText(item.getName()); setGraphic(null); } else { setText(null); } } }; } }); } public static void main(String[] args) { Application.launch(args); } }
OK you think, strings are easy and we kind of hacked the value in the selected field. After a bit more investigation I figured out that what I so call selected field is the button field. We can add a custom cell too like for the items. We can do the same procedure like for the ComoBox.setSellFactory(). Now I'm going to do that for both with a more sophisticated example.
Selections
Until now the ComboBox is not doing anything. How do we know which Member the user selects? As we can see in the code above, we used the ComboBox.getSelectionModel().selectFirst() method to select the first Member in the list. The selection model handles everything in terms of selecting. We have to ways to get the selected item:- Using the ComboBox.getSelectionModel().getSelectedItem() and ComboBox.getSelectionModel().getSelectedIndex()
- Using Listener to the selectedItems or selectedIndex.
Selection using Listener
We have to add a listener to that to track the users selections. Here the code:comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Member>() { @Override public void changed(ObservableValue<? extends Member> observable, Member oldValue, Member newValue) { System.out.println("Member: " + newValue.getName()); } });
Now everytime the user selects something in the ComboBox the listener will be called and we have access to the previous selected item and the current selected item. The good thing is really that we get the objects itself so we can use it immediately.