绑定到 TableView 的 ObservableMap 不更新新值
ObservableMap bound to TableView not updating new values
我有一个 ObservableMap
,我想将其绑定到 table 视图,以显示地图值具有的一些属性,但地图上的新值未显示。在 SO 上搜索和谷歌搜索我发现大部分相同的代码但没有解决方案。
这是一个解释问题的 SSCCE,我发现的唯一 'solution':使用地图的值创建一个新的 ObservableList
并将其设置为 table 每次地图都会改变,这不是一个很好的解决方案。
public class MapTableBindTest extends Application {
int personIDCounter;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
personIDCounter = 0;
// Map - TableView binding
ObservableMap<Integer, Person> map = FXCollections.observableHashMap();
Person first = new Person("First", "Person"); // this person is mapped before the bind to the table
map.put(first.getID(), first); // and because that, is shown on it
ObservableList<Map.Entry<Integer, Person>> list = FXCollections.observableArrayList(map.entrySet());
TableView<Map.Entry<Integer, Person>> table = new TableView<>(list);
Person second = new Person("Second", "Person"); // once the bind has been setted no new items
map.put(second.getID(), second); // are shown on the table. Same case when added in the UI
// Columns
TableColumn<Map.Entry<Integer, Person>, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(cellData -> cellData.getValue().getValue().getName());
TableColumn<Map.Entry<Integer, Person>, String> lastNameCol = new TableColumn<>("Surname");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue().getValue().getSurname());
table.getColumns().addAll(nameCol, lastNameCol);
// View
BorderPane root = new BorderPane();
TextField nameField = new TextField();
TextField lastNameField = new TextField();
Button addButton = new Button("Add");
addButton.setOnMouseClicked(event -> {
Person newP = new Person(nameField.getText(), lastNameField.getText());
map.put(newP.getID(), newP); // new items are not populated in the table...
nameField.clear();
lastNameField.clear();
// ... unless a new observable list is created and setted to the table
ObservableList<Map.Entry<Integer, Person>> auxiliarList = FXCollections.observableArrayList(map.entrySet());
table.setItems(auxiliarList);
// commenting this two lines result with no updates shown in the table
});
HBox TextHB = new HBox(nameField, lastNameField, addButton);
root.setTop(TextHB);
root.setCenter(table);
Scene scene = new Scene(root,400,500);
primaryStage.setScene(scene);
primaryStage.show();
}
// Sample class with some properties
class Person {
private int id;
private StringProperty name, surname;
public Person(String name, String surname) {
id = personIDCounter++;
this.name = new SimpleStringProperty(name);
this.surname = new SimpleStringProperty(surname);
}
public int getID() {
return id;
}
public StringProperty getName() {
return name;
}
public StringProperty getSurname() {
return surname;
}
}
}
地图的 entryset()
与 ObservableList
之间的绑定可能有问题。 javadoc 说 entrySet()
of an ObservableMap
:
Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. (...)
这并不清楚,否则 TableView 将随着更改而更新。
解决方案(感谢James_D):
ObservableMap
adds/remove 上的此侦听器值在 ObservableList
中更改,因此显示在 table.
中
map.addListener((MapChangeListener.Change<? extends Integer, ? extends Person> c) -> {
if (c.wasAdded()) {
Person added = c.getValueAdded();
list.add(new AbstractMap.SimpleEntry<Integer, Person>(added.getID(), added));
} else if (c.wasRemoved()) {
Person removed = c.getValueRemoved();
list.remove(new AbstractMap.SimpleEntry<Integer, Person>(removed.getID(), removed));
}
});
当您向其中添加项目时,您的地图会发生变化,entrySet()
返回的集合也会发生变化。但是,该集合不是可观察集合,因此 ObservableList
不可能 "see" 发生任何变化。
相反,您需要将列表直接连接到地图。您可以简单地通过使 table 视图成为 TableView<Person>
然后向地图注册一个侦听器来做到这一点:
map.addListener((Change<? extends Integer, ? extends Person> c) -> {
if (c.wasAdded()) {
list.add(c.getValueAdded());
} else if (c.wasRemoved()) {
list.remove(c.getValueRemoved());
}
});
完整示例如下:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MapTableBindTest extends Application {
int personIDCounter;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
personIDCounter = 0;
// Map - TableView binding
ObservableMap<Integer, Person> map = FXCollections.observableHashMap();
Person first = new Person("First", "Person"); // this person is mapped before the bind to the table
map.put(first.getID(), first); // and because that, is shown on it
ObservableList<Person> list = FXCollections.observableArrayList(map.values());
TableView<Person> table = new TableView<>(list);
map.addListener((Change<? extends Integer, ? extends Person> c) -> {
if (c.wasAdded()) {
list.add(c.getValueAdded());
} else if (c.wasRemoved()) {
list.remove(c.getValueRemoved());
}
});
Person second = new Person("Second", "Person"); // once the bind has been setted no new items
map.put(second.getID(), second); // are shown on the table. Same case when added in the UI
// Columns
TableColumn<Person, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(cellData -> cellData.getValue().getName());
TableColumn<Person, String> lastNameCol = new TableColumn<>("Surname");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue().getSurname());
table.getColumns().addAll(nameCol, lastNameCol);
// View
BorderPane root = new BorderPane();
TextField nameField = new TextField();
TextField lastNameField = new TextField();
Button addButton = new Button("Add");
addButton.setOnMouseClicked(event -> {
Person newP = new Person(nameField.getText(), lastNameField.getText());
map.put(newP.getID(), newP); // new items are not populated in the table...
nameField.clear();
lastNameField.clear();
});
HBox TextHB = new HBox(nameField, lastNameField, addButton);
root.setTop(TextHB);
root.setCenter(table);
Scene scene = new Scene(root,400,500);
primaryStage.setScene(scene);
primaryStage.show();
}
// Sample class with some properties
class Person {
private int id;
private StringProperty name, surname;
public Person(String name, String surname) {
id = personIDCounter++;
this.name = new SimpleStringProperty(name);
this.surname = new SimpleStringProperty(surname);
}
public int getID() {
return id;
}
public StringProperty getName() {
return name;
}
public StringProperty getSurname() {
return surname;
}
}
}
我有一个 ObservableMap
,我想将其绑定到 table 视图,以显示地图值具有的一些属性,但地图上的新值未显示。在 SO 上搜索和谷歌搜索我发现大部分相同的代码但没有解决方案。
这是一个解释问题的 SSCCE,我发现的唯一 'solution':使用地图的值创建一个新的 ObservableList
并将其设置为 table 每次地图都会改变,这不是一个很好的解决方案。
public class MapTableBindTest extends Application {
int personIDCounter;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
personIDCounter = 0;
// Map - TableView binding
ObservableMap<Integer, Person> map = FXCollections.observableHashMap();
Person first = new Person("First", "Person"); // this person is mapped before the bind to the table
map.put(first.getID(), first); // and because that, is shown on it
ObservableList<Map.Entry<Integer, Person>> list = FXCollections.observableArrayList(map.entrySet());
TableView<Map.Entry<Integer, Person>> table = new TableView<>(list);
Person second = new Person("Second", "Person"); // once the bind has been setted no new items
map.put(second.getID(), second); // are shown on the table. Same case when added in the UI
// Columns
TableColumn<Map.Entry<Integer, Person>, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(cellData -> cellData.getValue().getValue().getName());
TableColumn<Map.Entry<Integer, Person>, String> lastNameCol = new TableColumn<>("Surname");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue().getValue().getSurname());
table.getColumns().addAll(nameCol, lastNameCol);
// View
BorderPane root = new BorderPane();
TextField nameField = new TextField();
TextField lastNameField = new TextField();
Button addButton = new Button("Add");
addButton.setOnMouseClicked(event -> {
Person newP = new Person(nameField.getText(), lastNameField.getText());
map.put(newP.getID(), newP); // new items are not populated in the table...
nameField.clear();
lastNameField.clear();
// ... unless a new observable list is created and setted to the table
ObservableList<Map.Entry<Integer, Person>> auxiliarList = FXCollections.observableArrayList(map.entrySet());
table.setItems(auxiliarList);
// commenting this two lines result with no updates shown in the table
});
HBox TextHB = new HBox(nameField, lastNameField, addButton);
root.setTop(TextHB);
root.setCenter(table);
Scene scene = new Scene(root,400,500);
primaryStage.setScene(scene);
primaryStage.show();
}
// Sample class with some properties
class Person {
private int id;
private StringProperty name, surname;
public Person(String name, String surname) {
id = personIDCounter++;
this.name = new SimpleStringProperty(name);
this.surname = new SimpleStringProperty(surname);
}
public int getID() {
return id;
}
public StringProperty getName() {
return name;
}
public StringProperty getSurname() {
return surname;
}
}
}
地图的 entryset()
与 ObservableList
之间的绑定可能有问题。 javadoc 说 entrySet()
of an ObservableMap
:
Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. (...)
这并不清楚,否则 TableView 将随着更改而更新。
解决方案(感谢James_D):
ObservableMap
adds/remove 上的此侦听器值在 ObservableList
中更改,因此显示在 table.
map.addListener((MapChangeListener.Change<? extends Integer, ? extends Person> c) -> {
if (c.wasAdded()) {
Person added = c.getValueAdded();
list.add(new AbstractMap.SimpleEntry<Integer, Person>(added.getID(), added));
} else if (c.wasRemoved()) {
Person removed = c.getValueRemoved();
list.remove(new AbstractMap.SimpleEntry<Integer, Person>(removed.getID(), removed));
}
});
当您向其中添加项目时,您的地图会发生变化,entrySet()
返回的集合也会发生变化。但是,该集合不是可观察集合,因此 ObservableList
不可能 "see" 发生任何变化。
相反,您需要将列表直接连接到地图。您可以简单地通过使 table 视图成为 TableView<Person>
然后向地图注册一个侦听器来做到这一点:
map.addListener((Change<? extends Integer, ? extends Person> c) -> {
if (c.wasAdded()) {
list.add(c.getValueAdded());
} else if (c.wasRemoved()) {
list.remove(c.getValueRemoved());
}
});
完整示例如下:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MapTableBindTest extends Application {
int personIDCounter;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
personIDCounter = 0;
// Map - TableView binding
ObservableMap<Integer, Person> map = FXCollections.observableHashMap();
Person first = new Person("First", "Person"); // this person is mapped before the bind to the table
map.put(first.getID(), first); // and because that, is shown on it
ObservableList<Person> list = FXCollections.observableArrayList(map.values());
TableView<Person> table = new TableView<>(list);
map.addListener((Change<? extends Integer, ? extends Person> c) -> {
if (c.wasAdded()) {
list.add(c.getValueAdded());
} else if (c.wasRemoved()) {
list.remove(c.getValueRemoved());
}
});
Person second = new Person("Second", "Person"); // once the bind has been setted no new items
map.put(second.getID(), second); // are shown on the table. Same case when added in the UI
// Columns
TableColumn<Person, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(cellData -> cellData.getValue().getName());
TableColumn<Person, String> lastNameCol = new TableColumn<>("Surname");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue().getSurname());
table.getColumns().addAll(nameCol, lastNameCol);
// View
BorderPane root = new BorderPane();
TextField nameField = new TextField();
TextField lastNameField = new TextField();
Button addButton = new Button("Add");
addButton.setOnMouseClicked(event -> {
Person newP = new Person(nameField.getText(), lastNameField.getText());
map.put(newP.getID(), newP); // new items are not populated in the table...
nameField.clear();
lastNameField.clear();
});
HBox TextHB = new HBox(nameField, lastNameField, addButton);
root.setTop(TextHB);
root.setCenter(table);
Scene scene = new Scene(root,400,500);
primaryStage.setScene(scene);
primaryStage.show();
}
// Sample class with some properties
class Person {
private int id;
private StringProperty name, surname;
public Person(String name, String surname) {
id = personIDCounter++;
this.name = new SimpleStringProperty(name);
this.surname = new SimpleStringProperty(surname);
}
public int getID() {
return id;
}
public StringProperty getName() {
return name;
}
public StringProperty getSurname() {
return surname;
}
}
}