【问题标题】:Populating a TableView with a HashMap that will update when HashMap changes使用将在 HashMap 更改时更新的 HashMap 填充 TableView
【发布时间】:2016-09-07 09:31:30
【问题描述】:

我关注了这篇文章

Binding hashmap with tableview (JavaFX)

并创建了一个由 HashMap 中的数据填充的 TableView。

TableView 通过从map.entrySet() 创建一个ObservableList 并将ObservableList 交给TableView 的构造函数,从一个名为mapHashMap 接收其数据。 (代码如下)

然而,虽然它是 ObservableListSimpleStringPropertys,但当底层 HashMap 发生更改时,TableView 不会更新。

这是我的代码:

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();
    }

}

这正是来自Binding hashmap with tableview (JavaFX) 的代码,除了我添加了以下按钮:

        Button changeButton = new Button("Change");
        changeButton.setOnAction((ActionEvent e) -> {
            map.put("two", "2");
            System.out.println(map);
        });

然后我将其添加到带有 TableView 的 VBox 中。

当我点击按钮时,TableView 没有更新。但是,我可以验证底层HashMap 确实由于System.out.println(map) 输出而发生了变化。此外,当我单击TableView 中的列标题以按一列对数据进行排序时,重新排序表数据后会出现新的更新值。

如何在基础地图更改时自动更新表?

谢谢,

标记

【问题讨论】:

标签: java javafx hashmap tableview observablelist


【解决方案1】:

ObservableMap 与保持TableView 项目和映射键相同的侦听器一起使用,并将cellValueFactoryBindings.valueAt 一起使用,例如:

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);

【讨论】:

  • 谢谢。工作精美!有趣的方法,使它成为一个字符串表,并为键提供一个单独的 ObservableList。我也不知道 MapChangeListener 和 Bindings。很有帮助,谢谢!
  • 我试图理解 lambda 表达式中的绑定。假设我现在想为ObservableMap&lt;String, Shade&gt; 提供相同的功能,其中Shade 是一个自定义类,其中StringProperty 成员名为Description(即getDescription() 返回StringdescriptionProperty() 返回@ 987654334@) 我想在 column2 中显示。我将如何更改column2.setCellValueFactory(cd -&gt; Bindings.valueAt(map, cd.getValue())); 行?感谢您提供任何见解。
  • 不幸的是,这种 2 级绑定不是由绑定 API 提供的,因此您必须通过将侦听器添加到地图值的适当属性(并将其从旧值的属性,当值更改为不同的值时)。并不那么复杂,但是cmets部分不适合给出更详细的描述。
【解决方案2】:

这将更新您的 tableView。

changeButton.setOnAction(e -> {
                map.put("two", "2");
                table.getColumns().setAll(column1, column2);
                System.out.println(map);
});

【讨论】:

  • 谢谢。这确实有效。但是,如果我在表中添加一些东西怎么办?例如,如果我将行 map.put("two","2") 更改为 map.put("four","Four")。在这种情况下,表格不会更新以反映更改。为什么不呢?
  • 没关系,我现在明白为什么了。所以我可以将 table.getColumns().setAll(column1, column2) 行更改为 table.setItems(FXCollections.observableArrayList(map.entrySet())); 但这似乎不优雅,因为我正在重新创建一个 ArrayList 并用它重新填充表。此外,这样做将“破坏”用户可能已经通过单击列标题建立的任何排序顺序。还有其他方法吗?
  • 对于 >= 8u60 的版本,您可能应该将 getColumns().getColumns().setAll(column1, column2) 替换为 refresh()
猜你喜欢
  • 2015-03-23
  • 2016-08-02
  • 2013-12-19
  • 2021-07-07
  • 2023-03-16
  • 2012-11-16
  • 2012-01-10
  • 2014-03-07
  • 1970-01-01
相关资源
最近更新 更多