Combo-box key value pair in JavaFX 2

2019-01-07 18:44发布

I am just starting to learn JavaFX 2.
Now I am trying to build a sample application. Then I got stuck in combobox.
I did not find any reference to key value pair for combobox in JavaFX.
The combobox javadoc at http://docs.oracle.com/javafx/2/api/index.html does not say much about key value pair.

How can I create a combobox that has items which have different display value and actual value ?

标签: java javafx-2
3条回答
可以哭但决不认输i
2楼-- · 2019-01-07 18:57

You have 2 approaches:
1. Simply override the toString() method in your datamodel class. Example:

public class Demo extends Application {

    private final ObservableList<Employee> data =
            FXCollections.observableArrayList(
            new Employee("Azamat", 2200.15),
            new Employee("Veli", 1400.0),
            new Employee("Nurbek", 900.5));

    @Override
    public void start(Stage primaryStage) {

        ComboBox<Employee> combobox = new ComboBox<>(data);
        combobox.getSelectionModel().selectFirst(); // Select first as default
        combobox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Employee>() {

            @Override
            public void changed(ObservableValue<? extends Employee> arg0, Employee arg1, Employee arg2) {
                if (arg2 != null) {
                    System.out.println("Selected employee: " + arg2.getName());
                }
            }
        });
        StackPane root = new StackPane();
        root.getChildren().add(combobox);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }

    public static class Employee {
        private String name;
        private Double salary;

        @Override
        public String toString() {
            return name + " (sal:" + salary + ")";
        }

        public Employee(String name, Double salary) {
            this.name = name;
            this.salary = salary;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Double getSalary() {
            return salary;
        }

        public void setSalary(Double salary) {
            this.salary = salary;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Note the overriden toString() of the Employee class. The combobox's key (actual value) will be the Employee instance and the display value is employee.toString() in this case.
2. Second approach is to set cellFactory property of the combobox.

combobox.setCellFactory(new Callback<ListView<Employee>, ListCell<Employee>>() {

    @Override
    public ListCell<Employee> call(ListView<Employee> arg0) {
        return new ListCell<Employee>() {

            private final Button btn;
            {
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                btn = new Button();
            }

            @Override
            protected void updateItem(Employee item, boolean empty) {
                super.updateItem(item, empty);

                if (item == null || empty) {
                    setGraphic(null);
                } else {
                    btn.setStyle(item.getSalary() > 1000 ? "-fx-base:red" : "-fx-base: green");
                    btn.setText(item.getName() + "=" + item.getSalary());
                    setGraphic(btn);
                }
            }
        };
    }
});

This approach gives more powerful control over cell rendering. You can not just format display value but also include any node (control) into cell (button in this case) and add some viewing logic (item.getSalary()?"":"") too. The actual value remains the same ie Employee instance.

查看更多
我只想做你的唯一
3楼-- · 2019-01-07 18:57

I know that this question is old, but because I just spent hours in pointless digging on this issue it's a kind of mandatory for me to share the results. It seems that the ComboBox with overriden toString() method resolves the strings back to objects just right even if there are string duplicates in the list. The resolution must be index based and toString() is used only for presentation.

查看更多
唯我独甜
4楼-- · 2019-01-07 19:15

There is another solution, implementing a StringConverter. It's very useful for objects:

public class Product {

    private String code;
    private String name;

    public Product(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Converter implementation:

public class ProductConverter extends StringConverter<Product> {

    /** Cache of Products */
    private Map<String, Product> productMap = new HashMap<String, Product>();

    @Override
    public String toString(Product product) {
        productMap.put(product.getName(), product);
        return product.getName();
    }

    @Override
    public Product fromString(String name) {
        return productMap.get(name);
    }

}

Code in view:

    ComboBox<Product> cboProducts  = new ComboBox<Product>;
    cboProducts.setConverter(new ProductConverter());
    cboProducts.getItems().addAll(serviceManager.getProductList());

To get the value of the product, you can call getValue() method:

cboProducts.getValue()
查看更多
登录 后发表回答