I'm trying to use this to select a value from a Custom Combo Box:
import java.util.List;
import javafx.application.Application;
import static javafx.application.Application.launch;
import static javafx.application.Application.launch;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class MainApp extends Application
{
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage stage)
{
final ComboBox<ListGroupsObj> listGroups = new ComboBox();
listGroups.setButtonCell(new GroupListCell());
listGroups.setCellFactory(new Callback<ListView<ListGroupsObj>, ListCell<ListGroupsObj>>()
{
@Override
public ListCell<ListGroupsObj> call(ListView<ListGroupsObj> p)
{
return new GroupListCell();
}
});
listGroups.setEditable(true);
listGroups.setConverter..............
// Insert Some data
ListGroupsObj ob = ListGroupsObj.newInstance().groupId(12).groupName("Test");
listGroups.getItems().addAll(ob);
ListGroupsObj osb = ListGroupsObj.newInstance().groupId(13).groupName("Test2");
listGroups.getItems().addAll(osb);
listGroups.setValue(ob);
// Display the selected Group
listGroups.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ListGroupsObj>()
{
@Override
public void changed(ObservableValue<? extends ListGroupsObj> arg0, ListGroupsObj arg1, ListGroupsObj arg2)
{
if (arg2 != null)
{
System.out.println("Selected Group: " + arg1.getGroupId() + " - " + arg2.getGroupName());
}
}
});
final StackPane layout = new StackPane();
layout.getChildren().add(listGroups);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 15;");
stage.setScene(new Scene(layout));
stage.show();
}
class GroupListCell extends ListCell<ListGroupsObj>
{
@Override
protected void updateItem(ListGroupsObj item, boolean empty)
{
super.updateItem(item, empty);
if (item != null)
{
setText(item.getGroupId() + " - " + item.getGroupName());
}
}
}
private List<ListGroupsObj> listGroups;
public static class ListGroupsObj
{
private int groupId;
private String groupName;
public static ListGroupsObj newInstance()
{
return new ListGroupsObj();
}
public ListGroupsObj()
{
}
public ListGroupsObj groupId(int groupId)
{
this.groupId = groupId;
return this;
}
public ListGroupsObj groupName(String groupName)
{
this.groupName = groupName;
return this;
}
public int getGroupId()
{
return groupId;
}
public String getGroupName()
{
return groupName;
}
@Override
public String toString()
{
return groupId + " - " + groupName;
}
}
public class GroupConverter extends StringConverter<ListGroupsObj>
{
@Override
public String toString(ListGroupsObj obj)
{
return obj.getGroupId() + " - " + obj.getGroupName();
}
@Override
public ListGroupsObj fromString(String obj)
{
//TODO when you type for example "45 - NextGroup" you want to take only tyhe number"
return ListGroupsObj.newInstance().groupName(obj);
}
}
}
I get this error when I click outside of the comboBox:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.lang.String cannot be cast to com.selectmenuexample.MainApp$ListGroupsObj
I found that this can be done using convertor but I'm now aware how to use it. Can you help with this implementation?
Here is what is wrong:
Then you misunderstood how the ComboBox is working. If it's editable, the ComboBox will allow something to be typed inside the TextField. That's where the StringConverter comes. It will allow you to make the conversion between a String and your ListGroupsObj and the way around.
In order to go from a ListGroupsObj to a String, simply call the "toString()" method on your object.
But in the way around, you should either create a new ListGroupsObj, or verify that what's inside the ComboBox is not already one item of yours. For example, if you select an Item in the comboBox, the fromString() will be called. But you don't want to create a new ListGroupsObj, you just want to isolate the ListGroupsObj inside your items List and returns it.
Now, you have the guaranty that a call to getValue() on your ComboBox will always return an ListGroupsObj object since you have provided a custom and valid StringConverter.
Here is a simplified and working version of your code :
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class MainApp extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
final ComboBox<ListGroupsObj> comboBox = new ComboBox();
comboBox.setEditable(true);
comboBox.setConverter(new StringConverter<ListGroupsObj>() {
@Override
public String toString(ListGroupsObj obj) {
return obj.toString();
}
@Override
public ListGroupsObj fromString(String obj) {
//Here we try to identify if the given String actually represents one item of our list
for(ListGroupsObj tempObj:comboBox.getItems()){
if(tempObj.toString().equals(obj)){
return tempObj;
}
}
//If not we just create a new one
return ListGroupsObj.newInstance().groupName(obj);
}
});
// Insert Some data
ListGroupsObj ob = ListGroupsObj.newInstance().groupId(12).groupName("Test");
comboBox.getItems().addAll(ob);
ListGroupsObj osb = ListGroupsObj.newInstance().groupId(13).groupName("Test2");
comboBox.getItems().addAll(osb);
comboBox.setValue(ob);
final StackPane layout = new StackPane();
layout.getChildren().add(comboBox);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 15;");
stage.setScene(new Scene(layout));
stage.show();
}
public static class ListGroupsObj {
private int groupId;
private String groupName;
public static ListGroupsObj newInstance() {
return new ListGroupsObj();
}
public ListGroupsObj() {
}
public ListGroupsObj groupId(int groupId) {
this.groupId = groupId;
return this;
}
public ListGroupsObj groupName(String groupName) {
this.groupName = groupName;
return this;
}
public int getGroupId() {
return groupId;
}
public String getGroupName() {
return groupName;
}
@Override
public String toString() {
return groupId + " - " + groupName;
}
}
}
PS: The issue was already raised in the official JavaFX issue Tracker, I'll leave the link here since there is another example in the ticket (login required) : https://javafx-jira.kenai.com/browse/RT-29118
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With