我的大脑已经烧了,我找不到在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;
我希望很清楚......谢谢!
答案 0 :(得分:1)
TableView的数据结构是ObservableList<SomeObject>
,它与模型的数据结构不同,即Map<String, Map<String, String>>
。因此,您需要某种方法将模型数据结构转换为可在TableView中使用的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的更改(在您的情况下,这将对应于添加全新的陈述或移除现有的州)。
如果你需要在两个结构之间保持额外的同步性,例如动态地反映添加或删除变量,或者重命名变量或状态或更新变量值,那么你需要为内部应用额外的监听器 - 大多数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());
当然它想要一些带有未定义值的修复等等但最终会起作用:)