在我的控制器中的 JavaFX 中,单击“播放”按钮启动方法“createTTV()”后,创建嵌套的 TreeTableView(此 TreeTableView 会更大,但我不想粘贴太多不必要的代码)。该方法有许多 UI/非 UI 元素,无法将其拆分为 UI/非 UI。有没有一种方法可以在后台运行此类方法,并且当一切准备就绪时更新 UI 而不会锁定整个 UI?我下面的代码可以工作,但 UI 显然没有响应,直到 createTTV() 方法完成运行。
public void buildUi() {
vBoxForProgressIndicator.setVisible(false);
fuses_ttv.setRoot(root_FRCs);
btnPlay.setOnAction(event ->{
clear_hook();
vBoxForTableView.setVisible(false);
vBoxForProgressIndicator.setVisible(true);
new Thread(()->{
createTTV();
Platform.runLater(new Runnable() {
@Override
public void run() {
vBoxForProgressIndicator.setVisible(false);
vBoxForTableView.setVisible(true);
}
});
}).start();
});
}
private void createTTV(){
tdoStructure = model.getDataStructure();
allFRCs = tdoStructure.getAllFRCsAndCandidates();
allWires = tdoStructure.getAllWires();
allParts = tdoStructure.collectAllParts();
allFusesAndRelays = tdoStructure.getAllFusesAndRelais();
Platform.runLater(()->{
root_FRCs.getChildren().clear();
partDescription_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("description"));
partIdentification_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("identification"));
partNo_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("partNumber"));
slot_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("slot"));
pin_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("pin"));
moduleFamilyName_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("module"));
partType_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("type"));
variants_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("variants"));
if (frcElement != null) {
TreeItem frcTreeItem = new TreeItem<>(new FuseValidationTreeObject(frcElement));
if (!frcElement.getFrcSlots().isEmpty()) {
for (Map.Entry<String, ArrayList<String>> entrySlot : frcElement.getFrcSlots().entrySet()) {
String slot = entrySlot.getKey();
TreeItem slotItem = new TreeItem<>(new FuseValidationTreeObject(slot));
slotItem.setExpanded(true);
frcTreeItem.getChildren().add(slotItem);
for (Part fuseOrRelay : allFusesAndRelays) {
LinkedList<String> fuseOrRelayVariants = AppUtils.getAllVariantsForPart(fuseOrRelay);
if (fuseOrRelay.getReferencePartShortName().equals(frcElement.getShortName()) && AppUtils.getSlotIdFromFuseOrRelayName(fuseOrRelay.getShortName()).equals(slot)) {
TreeItem fuseRelayItem = new TreeItem(new FuseValidationTreeObject(fuseOrRelay));
boolean[] fuseOrRelayHasNoWires = {true};
//assign wires to fuse or relay
allWires.forEach(wire -> {
LinkedList<String> wireVariants = AppUtils.getAllVariantsForPart(wire);
if (fuseOrRelay.getReferencePartShortName().equals(wire.getWireToShortName())
&& entrySlot.getValue().contains(wire.getWireToPin()) && fuseOrRelay.getMemberModuleFamily().getDescription().equals(wire.getMemberModuleFamily().getDescription())) {
if (AppUtils.containsWireVariantsAnyOfFuseVariant(wireVariants, fuseOrRelayVariants)) {
TreeItem wireToItem = new TreeItem<>(new FuseValidationTreeObject(wire));
wireToItem.setExpanded(true);
fuseRelayItem.getChildren().add(wireToItem);
fuseOrRelayHasNoWires[0] = false;
if (!fuseOrRelayVariants.containsAll(wireVariants)) {
if (!wiresWithWrongVariants.contains(wire.getWireNumber())) {
wiresWithWrongVariants.add(wire.getWireNumber());
}
}
}
}
if (fuseOrRelay.getReferencePartShortName().equals(wire.getWireFromShortName())
&& entrySlot.getValue().contains(wire.getWireFromPin()) && fuseOrRelay.getMemberModuleFamily().getDescription().equals(wire.getMemberModuleFamily().getDescription())) {
if (AppUtils.containsWireVariantsAnyOfFuseVariant(wireVariants, fuseOrRelayVariants)) {
TreeItem wireFromItem = new TreeItem<>(new FuseValidationTreeObject(wire));
wireFromItem.setExpanded(true);
fuseRelayItem.getChildren().add(wireFromItem);
fuseOrRelayHasNoWires[0] = false;
if (!fuseOrRelayVariants.containsAll(wireVariants)) {
if (!wiresWithWrongVariants.contains(wire.getWireNumber())) {
wiresWithWrongVariants.add(wire.getWireNumber());
}
}
}
}
});
fuseRelayItem.setExpanded(true);
if (fuseOrRelayHasNoWires[0]) {
emptyFusesOrRelays.add(fuseOrRelay.getShortName());
}
slotItem.getChildren().add(fuseRelayItem);
}
}
if (slotItem.isLeaf()) {
//insert empty slot into emptySlots Map
if (emptySlots.containsKey(frcElement.getShortName())) {
emptySlots.get(frcElement.getShortName()).add(0, slot);
} else {
emptySlots.put(frcElement.getShortName(), new ArrayList<>(Arrays.asList(slot)));
}
//some more conditions
allWires.forEach(wire -> {
//if there is no fuse in the slot but there is still wire to fuse in this slot
if (wire.getWireToShortName().equals(frcElement.getShortName()) && entrySlot.getValue().contains(wire.getWireToPin())) {
wiresWithNoFuseOrRelay.add(wire);
TreeItem emptyWireTo = new TreeItem<>(new FuseValidationTreeObject(wire));
slotItem.getChildren().add(emptyWireTo);
}
if (wire.getWireFromShortName().equals(frcElement.getShortName()) && entrySlot.getValue().contains(wire.getWireFromPin())) {
wiresWithNoFuseOrRelay.add(wire);
TreeItem emptyWireFrom = new TreeItem<>(new FuseValidationTreeObject(wire));
slotItem.getChildren().add(emptyWireFrom);
}
});
}
}
}
frcTreeItem.setExpanded(true);
root_FRCs.getChildren().add(frcTreeItem);
}
}
}
如果 UI 元素未附加到 active 场景,则可以在另一个线程上创建。
对于大多数 JavaFX 控件(有一些例外,例如
WebView
),仅在 JavaFX 线程上创建或修改它们的规则仅适用于对附加到 Stage
的 live场景图的修改,其中正在显示,或者正在为其制作
snapshot
。只能在 JavaFX 线程上创建的控件在其文档中已列出。
例如,在另一个线程(例如在任务中)异步加载 FXML 文件并稍后将其附加到 JavaFX 应用程序线程上的场景图是完全可以的。
您可以使用
runLater
将创建的 TreeTableView
添加到活动场景中。
Task
文档,部分:“修改场景图的任务”。
一般来说,任务不应该直接与 UI 交互。这样做会在特定任务实现和 UI 的特定部分之间创建紧密耦合。但是,当您确实想要创建此类耦合时,必须确保使用
,以便场景图的任何修改都发生在 FX 应用程序线程上。Platform.runLater
在提供的示例中,UI 元素是在任务线程上创建的,
Platform.runLater()
仅用于将创建的元素添加到场景图中。
final Group group = new Group();
Task<Void> task = new Task<Void>() {
@Override protected Void call() throws Exception {
for (int i=0; i<100; i++) {
if (isCancelled()) break;
final Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
Platform.runLater(new Runnable() {
@Override public void run() {
group.getChildren().add(r);
}
});
}
return null;
}
};
或者,您可以在任务中创建
TreeTableView
并将其添加到场景 onSucceeded
所以你可以这样写:
Task<TreeTableView> task = new Task<TreeTableView>() {
@Override protected TreeTableView call() {
return createTreeTableView();
}
private TreeTableView createTreeTableView() {
// Create and return a new TreeTableView with all new TreeItems initialized.
}
};
task.setOnSucceeded(e ->
myParentNode.getChildren().add(
task.getValue()
)
);
或者,您可以在
TreeItems
中仅创建 Task
如果您仍然对在另一个线程中创建 UI 元素感到不舒服,那么您可以按照 James_D 的评论建议:
实例不是UI组件;它们只包含数据。您可以在后台线程上创建所有TreeItem
实例。然后只需创建树表、列,配置它们,并在 FX 应用程序线程上设置树表的根。后面的操作并不太耗时。TreeItem