将ComboBox#itemsProperty绑定到Map#keySet

时间:2015-12-25 02:23:23

标签: java javafx java-8 javafx-8

对于我正在做的事情,我需要将javafx.scene.control.ComboBox的成员设置为Map#keySet()集的成员。

ComboBox中可用的选项需要在更新Map的键时更新。 (基本上,我想使用ComboBox从Map中选择成员,这些成员将在运行时更新。)

很遗憾,ComboBox#itemsProperty().bind(ObservableValue<>)ComboBox#itemsProperty().set(ObservableList<>)都不会使用Set&lt;&gt;,因此将itemsProperty连接到Map#keySet的直接路径无效。

基本上: 我怎样才能使ComboBox的项目成为Map的keySet的成员? 同样,我需要的行为是ComboBox的项目将反映我KeySet的{​​{1}}。 (Map可以是Map的任何实现。)

编辑:问题似乎是在Map中创建ObservableList - 在本例中为Collection - 而不仅仅是一个副本,而是一个引用SetSet将反映ObservableList的内容。

1 个答案:

答案 0 :(得分:3)

如果您有ObservableMap,则可以为其添加监听器。例如:

ComboBox<String> comboBox = ... ;
ObservableMap<String, Something> map = ... ;
map.addListener((MapChangeListener.Change<? extends String, ? extends Something> c) -> 
    comboBox.getItems().setAll(map.keySet()));

您也可以使用绑定来执行此操作,但我认为它不是更清洁:

comboBox.itemsProperty().bind(Bindings.createObjectBinding(() -> 
    FXCollections.observableArrayList(map.keySet()),
    map);

这是一个SSCCE,展示了这两种技术:

import java.util.concurrent.atomic.AtomicInteger;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class BindItemsToKeySet extends Application {

    @Override
    public void start(Stage primaryStage) {
        ObservableMap<Integer, Item> itemLookupById = FXCollections.observableHashMap();
        ComboBox<Integer> listenerCombo = new ComboBox<>();
        ComboBox<Integer> bindingCombo = new ComboBox<>();

        itemLookupById.addListener((MapChangeListener.Change<? extends Integer, ? extends Item> c) -> 
            listenerCombo.getItems().setAll(itemLookupById.keySet())
        );

        bindingCombo.itemsProperty().bind(Bindings.createObjectBinding(() ->
            FXCollections.observableArrayList(itemLookupById.keySet()), 
            itemLookupById));

        TextField textField = new TextField();
        textField.setOnAction(e -> {
            if (textField.getText().isEmpty()) {
                return ;
            }
            Item item = new Item(textField.getText());
            itemLookupById.put(item.getId(), item);
            textField.clear();
        });

        textField.setTooltip(new Tooltip("Type an item name and press enter"));

        VBox root = new VBox(10, 
                textField, 
                listenerCombo, 
                bindingCombo);

        root.setPadding(new Insets(10));

        root.setAlignment(Pos.CENTER);
        primaryStage.setScene(new Scene(root, 250, 350));
        primaryStage.show();

    }

    public static class Item {
        private final int id ;
        private final String name ;

        private final static AtomicInteger nextID = new AtomicInteger(1000);

        public Item(String name) {
            this.id = nextID.incrementAndGet();
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public String getName() {
            return name;
        }

    }

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