Java 将ComboBoxitemsProperty绑定到MapkeySet

Java 将ComboBoxitemsProperty绑定到MapkeySet,java,javafx,java-8,javafx-8,Java,Javafx,Java 8,Javafx 8,对于我正在做的事情,我需要将javafx.scene.control.ComboBox的成员设置为MapkeySet集合的成员 组合框中可用的选项需要随着地图的键的更新而更新。基本上,我想使用组合框从映射中选择成员,这将在运行时更新 不幸的是,ComboBoxitemsProperty.BindObservalEvalue和ComboBoxitemsProperty.SetObservalElist都不会使用集合,因此将itemsProperty连接到MapkeySet的直接路径不起作用 基本上

对于我正在做的事情,我需要将javafx.scene.control.ComboBox的成员设置为MapkeySet集合的成员

组合框中可用的选项需要随着地图的键的更新而更新。基本上,我想使用组合框从映射中选择成员,这将在运行时更新

不幸的是,ComboBoxitemsProperty.BindObservalEvalue和ComboBoxitemsProperty.SetObservalElist都不会使用集合,因此将itemsProperty连接到MapkeySet的直接路径不起作用

基本上: 如何使组合框的项成为映射键集的成员? 同样,我需要的行为是组合框的项将反映我的映射的键集。Map可以是Map的任何实现

编辑:问题似乎是从集合(在本例中为集合)中创建ObservableList,而不是将其作为副本,而是对集合的引用,以便ObservableList反映集合的内容。

如果您有ObservableMap,您可以向其添加侦听器。例如:

ComboBox<String> comboBox = ... ;
ObservableMap<String, Something> map = ... ;
map.addListener((MapChangeListener.Change<? extends String, ? extends Something> c) -> 
    comboBox.getItems().setAll(map.keySet()));
下面是一个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);
    }
}

你能用可观察地图作为地图吗?是的,地图可以是可观察地图。但是,将itemsProperty绑定或设置为ObservableList;我需要一个ObservableList。您可以向ObservableMap添加一个侦听器,或者创建一个自定义绑定。请看答案。我不知道我做错了什么,但第一个添加组合框作为ObservaleMap的侦听器。不过,我还是能够让绑定工作。谢谢我直接在这里输入,没有测试,所以可能有错误。我会在有机会的时候检查的。是的,我把更改的类型参数搞错了。修复并添加了一个示例。很好的解决方案!
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);
    }
}