绑定到TableView的ObservableMap不更新新值

时间:2015-11-17 17:57:56

标签: java javafx binding

我想要绑定到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

1 个答案:

答案 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;
        }
    }
}