Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast String in JavaFX ComboBox

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?

like image 304
Peter Penzov Avatar asked Oct 01 '22 08:10

Peter Penzov


1 Answers

Here is what is wrong:

  • You called your ComboBox listGroups and your List of Items listGroups. So in your start code, you were hiding that variable. So I removed that useless variable since you can manipulate Items directly in the ComboBox.
  • You had basically three methods/variable doing the exact same things. Converting your Object into a String with a "-" between them. So I removed the GroupConverter and the custom cellFactory. You don't need them because you already have your "toString()" method in your ListGroupsObj which is doing the job.

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

like image 143
Maxoudela Avatar answered Oct 05 '22 10:10

Maxoudela