I have an ObservableMap<String, ObservableSet<String>>
.
I would like to create a UI which has a comboBox and a ListView. The comboBox is populated by the keys of the map. Selecting one key from the map would then populate the ListView with the contents of the Set that is mapped to by that key.
In the past I have handled making a ListView for an ObservableSet by creating a second data structure, an ObservableList, and adding a ChangeListener to the set that updates the ObservableList so that it mirrors the set.
However in this case I don't have just one set, but a map of many sets. See my previous question which is similar but simpler: JavaFX: Populate TableView with an ObservableMap that has a custom class for its values
Here is some sample runnable code. It provides most of the functionality I want. However, the ListView doesn't respond to changes in the underlying Map of Sets. In this example, if you select "Vehicles" from the ComboBox and then click the Change Vehicles button, no changes are reflected in the ListView. However if you then select "Colors" and then back to "Vehicles", the ListView is repopulated and you now see the change.
So how would one get the ListView to automatically update itself when the underlying Map of Sets changes? My first guess is that you need to add a Listener to each Set that maintains a mirror of the contents of each Set in an ObservableList. But since this is a Map of Sets, the number of Sets can change and so the number of mirroring Lists will need to change. So I would need to have a collection of ObservableLists, I suppose...? And every time a new element is added to the Map, a new Listener and new ObservableList will need to be constructed.
import java.util.Map;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.collections.ObservableSet;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MapSetView extends Application {
ObservableMap<String, ObservableSet<String>> map = FXCollections.observableHashMap();
ComboBox<String> keysCombo = new ComboBox<>();
ListView<String> valuesList = new ListView<>();
@Override
public void start(Stage stage) throws Exception {
map.put("Vehicle", FXCollections.observableSet("plane", "train", "automobile"));
map.put("Color", FXCollections.observableSet("black","blue","red"));
keysCombo.getItems().clear();
for(Map.Entry<String, ObservableSet<String>> varEntry : map.entrySet()) {
keysCombo.getItems().add(varEntry.getKey());
}
keysCombo.setOnAction( (ActionEvent e) -> {
String selectedName = keysCombo.getSelectionModel().getSelectedItem();
valuesList.setItems(FXCollections.observableArrayList(map.get(selectedName)));
});
Button changeVehicles = new Button("Change Vehicles");
changeVehicles.setOnAction( (ActionEvent e) -> {
map.get("Vehicle").add("boat");
});
// display UI
VBox vBox = new VBox(8);
vBox.getChildren().addAll(keysCombo, valuesList, changeVehicles);
Scene scene = new Scene(vBox, 400, 400);
stage.setScene(scene);
stage.show();
}
}