使用Map <String,Map <String,String>填充TableView JavaFX

我的大脑已经在燃烧,我找不到在JavaFX中填充TableView的正确方法。 我的数据地图是Map<String, Map<String, String>> 。 第一个键是一个状态名,value是将键作为变量并将值作为变量值的映射。 我需要一张表

| States  | x | y | ... 
| state 1 | 5 | 6 | ...

等等

编辑:这是我最后的解决方案,填充只有一列和其他填充相同的数据。 这可能是另一个带有价值的foreach。

for (TableColumn<ObservableList<String>, ?> column : table.getColumns()) {
            TableColumn<ObservableList<String>, String> col = (TableColumn<ObservableList<String>, String>) column;
            col.setCellValueFactory(data -> new ReadOnlyObjectWrapper<>(someValue));
        }

我想着这样的解决方案,但它只填充最后一个值的行:

ObservableList<ObservableList<String>> tableData = FXCollections.observableArrayList();
for (Map<String, String> map : map.values()) {
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    if (Utils.getTableColumnByName(table, entry.getKey()) != null) {
                        TableColumn<ObservableList<String>, String> column = (TableColumn<ObservableList<String>, String>) Utils.getTableColumnByName(table, entry.getKey());
                        column.setCellValueFactory(data -> new ReadOnlyObjectWrapper<>(entry.getValue()));
                    }
                }
            }
for (Integer stateIndex : states) {
                tableData.add(FXCollections.observableArrayList("state " + stateIndex));
            }
table.setItems(tableData);

我正在寻找任何建议,没有完整的解决方案:)

编辑2:有了这个,我只在开始执行时填充第一行。 我不知道在完成执行后如何填充另一行。 这是在foreach:

TableColumn<ObservableList<String>, String> varColumn = new TableColumn();
                varColumn.setText(variable.getText());
                varColumn.setCellValueFactory(data -> new ReadOnlyObjectWrapper<>(value.getText()));
                table.getColumns().add(varColumn);

而这之后的foreach:

table.setItems(getTableData());

和getTableData():

ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();

    for (String row : map.keySet()) {
        data.add(FXCollections.observableArrayList(row));
    }

    return data;

我希望这很清楚......谢谢!


TableView的数据结构是一个ObservableList<SomeObject> ,它与您的模型的数据结构不同,它是Map<String, Map<String, String>> 。 因此,您需要一些方法将模型数据结构转换为可在TableView中使用的ObservableList。

我可以想到的几个方法是:

  • 在列表中创建一组虚拟对象,每行对应一个模型中的实际项目,并提供细胞价值工厂,它们动态地将需要的数据从模型中拉出。
  • 创建一个并行的ObservableList数据结构,并根据需要同步模型和ObservableList之间的基础数据。
  • 上面的选项2是我在这里提供的示例。 它是一种MVVM(模型,视图,视图模型)架构方法。 该模型是基础的基于地图的结构,视图模型是由TableView视图消耗的可观察列表。

    这是一个示例。

    状态变化

    import javafx.application.Application;
    import javafx.collections.*;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.stage.Stage;
    
    import java.util.Map;
    import java.util.Random;
    import java.util.stream.Collectors;
    
    public class StateView extends Application {
        @Override
        public void start(Stage stage) {
            ObservableMap<String, ObservableMap<String, String>> states = populateStates();
    
            final TableView<StateItem> tableView = new TableView<>();
            tableView.setItems(extractItems(states));
    
            final TableColumn<StateItem, String> stateCol    = new TableColumn<>("State");
            final TableColumn<StateItem, String> variableCol = new TableColumn<>("Variable");
            final TableColumn<StateItem, String> valueCol    = new TableColumn<>("Value");
    
            stateCol.setCellValueFactory(new PropertyValueFactory<>("stateName"));
            variableCol.setCellValueFactory(new PropertyValueFactory<>("variableName"));
            valueCol.setCellValueFactory(new PropertyValueFactory<>("variableValue"));
    
            tableView.getColumns().setAll(stateCol, variableCol, valueCol);
    
            states.addListener((MapChangeListener<String, ObservableMap<String, String>>) change -> 
                    tableView.setItems(extractItems(states))
            );
    
            Scene scene = new Scene(tableView);
            stage.setScene(scene);
            stage.show();
        }
    
        private ObservableList<StateItem> extractItems(ObservableMap<String, ObservableMap<String, String>> states) {
            return FXCollections.observableArrayList(
                    states.keySet().stream().sorted().flatMap(state -> {
                        Map<String, String> variables = states.get(state);
                        return variables.keySet().stream().sorted().map(
                                variableName -> {
                                    String variableValue = variables.get(variableName);
                                    return new StateItem(state, variableName, variableValue);
                                }
                        );
                    }).collect(Collectors.toList())
            );
        }
    
        private static final Random random = new Random(42);
        private static final String[] variableNames = { "red", "green", "blue", "yellow" };
    
        private ObservableMap<String, ObservableMap<String, String>> populateStates() {
            ObservableMap<String, ObservableMap<String, String>> states = FXCollections.observableHashMap();
            for (int i = 0; i < 5; i ++) {
                ObservableMap<String, String> variables = FXCollections.observableHashMap();
    
                for (String variableName: variableNames) {
                    variables.put(variableName, random.nextInt(255) + "");
                }
    
                states.put("state " + i, variables);
            }
    
            return states;
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        public static class StateItem {
            private String stateName;
            private String variableName;
            private String variableValue;
    
            public StateItem(String stateName, String variableName, String variableValue) {
                this.stateName = stateName;
                this.variableName = variableName;
                this.variableValue = variableValue;
            }
    
            public String getStateName() {
                return stateName;
            }
    
            public void setStateName(String stateName) {
                this.stateName = stateName;
            }
    
            public String getVariableName() {
                return variableName;
            }
    
            public void setVariableName(String variableName) {
                this.variableName = variableName;
            }
    
            public String getVariableValue() {
                return variableValue;
            }
    
            public void setVariableValue(String variableValue) {
                this.variableValue = variableValue;
            }
        }
    }
    

    我所做的是提供一个新的StateItem类,它提供给视图模型的可观察列表,并包含用于表的每一行的stateName,variableName和variableValue值。 有一个单独的提取功能,从模型映射中提取数据并根据需要填充视图模型可观察列表。

    什么“根据需要”意味着你将取决于你需要完成什么。 如果您只需要在初始化时预先填充数据,则只需调用一次将数据提取到视图模型即可。

    如果您需要视图模型根据底层数据的变化动态更改,那么您需要:

  • 将视图模型中的值绑定到模型OR
  • 为模型添加一些监听器,然后用它更新视图模型OR
  • 确保您在底层模型更改时直接调用更新视图模型。
  • 对于示例,我提供了一个基于侦听器的方法的示例。 我将基础模型类从Map<String, Map<String, String>更改为ObservableMap<String, ObservableMap<String, String>> ,然后使用MapChangeListener监听最外面的ObservableMap的更改(在您的情况下,它将对应增加一个全新的状态或移除现有状态)。

    如果需要保持两个结构之间的附加同步性,例如动态地反映变量被添加或删除,或者变量或状态被重命名或变量值被更新,那么您将需要为最内部的ObservableMap应用附加的侦听器,正在维护您的变量列表。 您可能还会将类型从String更改为StringProperty,以便您可以将模型视图StateItem类中的值绑定到模型中的值,并且还可以将属性访问器添加到StateItem类。

    无论如何,上述代码不太可能完全解决您的问题,但可能有助于更好地理解您可能希望评估解决此问题的潜在方法。

    另外,也许使用TreeTableView,可能比TableView更适合您的实现。 只取决于你的需求。


    多谢你们! 我做的! JavaFX中的表格非常烦人,但我的解决方案适用于任何需要它的人:)

    public class Row {
    
    private String state;
    
    private String[] values;
    
    public Row(String state, String... values) {
        this.state = state;
        this.values = values;
    }
    
    public String getState() {
        return state;
    }
    
    public List<String> getValues() {
        return Arrays.asList(values);
    }
    

    设置栏:

    column.setCellValueFactory(data -> new ReadOnlyObjectWrapper<>(data.getValue().getValues().get(index)));
    

    从地图获取数据:

    public ObservableList<Row> getTableData() {
        ObservableList<Row> data = FXCollections.observableArrayList();
    
        for (Map.Entry<String, TreeMap<String, String>> entry : map.entrySet()) {
            String[] values = new String[entry.getValue().values().size()];
            int index = 0;
            for (String value : entry.getValue().values()) {
                values[index] = value;
                index++;
            }
            Row row = new Row(entry.getKey(), values);
            data.add(row);
        }
    
        return data;
    }
    

    最后:

    table.setItems(getTableData());
    

    具有预期输出的表格

    当然,它需要一些未定义值的修补程序等等,但它最终会起作用:)

    链接地址: http://www.djcxy.com/p/80153.html

    上一篇: Populate TableView with Map<String, Map<String, String>> JavaFX

    下一篇: How to populate TableView with rows of List<Object>?