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值。有一个单独的提取功能,可以从模型映射中提取数据,并根据需要填充视图模型可观察列表。
“按需”对你意味着什么将取决于你需要完成什么。如果只需要在初始化时预先填充数据,那么只需调用一次即可将数据提取到视图模型。
如果需要根据基础数据的更改动态更改视图模型,则需要:
-
执行从视图模型到模型或
-
添加一些侦听器以更改模型,然后使用这些侦听器更新视图模型或
-
确保在基础模型更改时直接调用以更新视图模型。
对于示例,我提供了一个基于侦听器的方法的示例。我将基础模型类从
Map<String, Map<String, String>
到
ObservableMap<String, ObservableMap<String, String>>
然后使用
MapChangeListener
监听最外层ObservableMap的更改(在您的情况下,这将对应于添加一个全新状态或删除一个现有状态)。
如果需要在两个结构之间保持额外的同步性,例如动态地反映变量的添加或删除、变量或状态的重命名或变量值的更新,那么需要为维护变量列表的最内部可观察映射应用额外的侦听器。您可能还将类型从String更改为StringProperty,以便可以将模型视图StateItem类中的值绑定到模型中的值,并且还可以添加
property accessors
到StateItem类。
无论如何,上述代码不太可能完全解决您的问题,但可能有助于更好地理解您可能希望评估以解决问题的潜在方法。
另一方面,使用TreeTableView可能比使用TableView更好地控制您的实现。取决于你的需要。