我想要绑定到tableview的ObservableMap
,以显示映射值的某些属性,但不会显示地图上的新值。搜索SO和谷歌搜索我发现大多数相同的代码,但没有解决方案。
Heres是一个解释问题的SSCCE,只有'解决方案'我发现:使用地图的值创建一个新的ObservableList
,并将其设置为每次 地图更改的表格,这不是一个很好的解决方案。
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()
的{{1}}:
返回此映射中包含的映射的Set视图。 该集由地图支持,因此对地图的更改会反映在集中,反之亦然。(...)
哪个不明显,否则TableView会随着更改而更新。
解决方案(感谢James_D):
ObservableMap
上的此监听器添加/删除了ObservableMap
中的值更改,因此显示在表中。
ObservableList
答案 0 :(得分:0)
当您向其添加项目时,您的地图正在发生变化,entrySet()
返回的集合也会发生变化。但是,该集合不是可观察的集合,因此ObservableList
无法“看到”发生的任何更改。
相反,您需要直接将列表连接到地图。您只需将表视图设为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;
}
}
}