问题
I have followed this post
Binding hashmap with tableview (JavaFX)
and created a TableView that is populated by data from a HashMap.
The TableView
receives its data from a HashMap
called map
by creating an ObservableList
from map.entrySet()
and handing that ObservableList
off to the TableView
's constructor. (Code is below)
However, although it's an ObservableList
with SimpleStringProperty
s, the TableView does not update when changes are made to the underlying HashMap.
Here is my code:
public class MapTableView extends Application {
@Override
public void start(Stage stage) throws Exception {
try {
// sample data
Map<String, String> map = new HashMap<>();
map.put("one", "One");
map.put("two", "Two");
map.put("three", "Three");
// use fully detailed type for Map.Entry<String, String>
TableColumn<Map.Entry<String, String>, String> column1 = new TableColumn<>("Key");
column1.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) {
// this callback returns property for just one cell, you can't use a loop here
// for first column we use key
return new SimpleStringProperty(p.getValue().getKey());
}
});
TableColumn<Map.Entry<String, String>, String> column2 = new TableColumn<>("Value");
column2.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) {
// for second column we use value
return new SimpleStringProperty(p.getValue().getValue());
}
});
ObservableList<Map.Entry<String, String>> items = FXCollections.observableArrayList(map.entrySet());
final TableView<Map.Entry<String,String>> table = new TableView<>(items);
table.getColumns().setAll(column1, column2);
Button changeButton = new Button("Change");
changeButton.setOnAction((ActionEvent e) -> {
map.put("two", "2");
System.out.println(map);
});
VBox vBox = new VBox(8);
vBox.getChildren().addAll(table, changeButton);
Scene scene = new Scene(vBox, 400, 400);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch();
}
}
This is exactly the code from Binding hashmap with tableview (JavaFX) except I have added the following button:
Button changeButton = new Button("Change");
changeButton.setOnAction((ActionEvent e) -> {
map.put("two", "2");
System.out.println(map);
});
which I then add to a VBox with the TableView.
When I click the button, the TableView
is not updated. However, I can verify that the underlying HashMap
has indeed changed because of the System.out.println(map)
output. Also when I click on a column header in the TableView
to sort the data by one column, the new updated value appears after the table data is re-sorted.
How can I make the table update automatically when the underlying map is changed?
Thank you,
Mark
回答1:
Use an ObservableMap
with a listener that keeps the TableView
items and the keys of the map the same and use the cellValueFactory
with Bindings.valueAt, e.g.:
ObservableMap<String, String> map = FXCollections.observableHashMap();
ObservableList<String> keys = FXCollections.observableArrayList();
map.addListener((MapChangeListener.Change<? extends String, ? extends String> change) -> {
boolean removed = change.wasRemoved();
if (removed != change.wasAdded()) {
// no put for existing key
if (removed) {
keys.remove(change.getKey());
} else {
keys.add(change.getKey());
}
}
});
map.put("one", "One");
map.put("two", "Two");
map.put("three", "Three");
final TableView<String> table = new TableView<>(keys);
TableColumn<String, String> column1 = new TableColumn<>("Key");
// display item value (= constant)
column1.setCellValueFactory(cd -> Bindings.createStringBinding(() -> cd.getValue()));
TableColumn<String, String> column2 = new TableColumn<>("Value");
column2.setCellValueFactory(cd -> Bindings.valueAt(map, cd.getValue()));
table.getColumns().setAll(column1, column2);
回答2:
This will update your tableView.
changeButton.setOnAction(e -> {
map.put("two", "2");
table.getColumns().setAll(column1, column2);
System.out.println(map);
});
来源:https://stackoverflow.com/questions/37171820/populating-a-tableview-with-a-hashmap-that-will-update-when-hashmap-changes