使用Map填充TableView <string,map <string,=“”string =“”>&gt; JavaFX的

时间:2018-02-20 16:15:55

标签: javafx tableview javafx-8

我的大脑已经烧了,我找不到在JavaFX中填充TableView的正确方法。我的数据地图是Map<String, Map<String, String>>。第一个键是状态名称,值是map,其中键作为变量,值作为变量值。我需要一张像

这样的表格
| States  | x | y | ... 
| state 1 | 5 | 6 | ...

编辑:这是我的最后一个解决方案,它只填充一列,而其他列则填充相同的数据。这可能是另一个有价值观的预言。

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;

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

2 个答案:

答案 0 :(得分:1)

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

我能想到的几种方法是:

  1. 创建一组虚拟对象,这些对象位于列表中,每行一个对应于模型中的实际项目,并提供单元值工厂,从而动态地从模型中提取所需数据。
  2. 创建一个并行的ObservableList数据结构,并根据需要同步模型和ObservableList之间的基础数据。
  3. 以上选项2是我在这里提供的样本。它是一种MVVM(模型,视图,视图模型)架构方法。该模型是您基于地图的基础结构,视图模型是视图所使用的可观察列表,即TableView。

    以下是一个示例。

    state changes

    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值。有一个单独的提取功能,它从模型图中提取数据,并根据需要填充视图模型可观察列表。

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

    如果您需要根据对基础数据的更改动态更改视图模型,则需要:

    1. 执行视图模型中的值绑定到模型OR
    2. 为模型的更改添加一些侦听器,然后用于更新视图模型或
    3. 确保在基础模型发生更改时直接调用以更新视图模型。
    4. 对于示例,我提供了一个基于侦听器的方法的示例。我将基础模型类从Map<String, Map<String, String>更改为ObservableMap<String, ObservableMap<String, String>>,然后使用MapChangeListener来监听最外层ObservableMap的更改(在您的情况下,这将对应于添加全新的陈述或移除现有的州)。

      如果你需要在两个结构之间保持额外的同步性,例如动态地反映添加或删除变量,或者重命名变量或状态或更新变量值,那么你需要为内部应用额外的监听器 - 大多数ObservableMap都在维护你的变量列表。您可能还会将类型从String更改为StringProperty,以便您可以将模型视图StateItem类中的值绑定到模型中的值,还可以将property accessors添加到StateItem类。

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

      另外,也许使用TreeTableView可能比TableView更好地控制您的实现。只是取决于你的需求。

答案 1 :(得分:0)

谢谢你们!我做的! 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());

Table with expected output

当然它想要一些带有未定义值的修复等等但最终会起作用:)