JavaFX:在进程运行时禁用所有组件,并显示进度指示器
问题描述:
我有一个方法从数据库读取值并返回Map<Integer,String>.
此方法需要一些时间才能返回映射。JavaFX:在进程运行时禁用所有组件,并显示进度指示器
直到时间值被读取我想要一个进度指示器(只有加载环像指示器就足够了,不需要进度条)在屏幕上显示,所有其他组件应该被禁用,直到时间进度条是所示。
public void scanDevice() {
ObservableList<TextField> list = FXCollections.observableArrayList(vehicleId, vehicleName, deviceType,
offboardBroker1, offboardBroker2, postfixQueue, pKIServer);
editedValuesMap.clear();
// devicePlugged = true;
if (cbChooseProject.getSelectionModel().getSelectedItem() != null) {
try {
devicePlugged = dsAdapter.getAdapter();
if (devicePlugged) {
if (bScanDevice.isFocused()) {
readMap = new HashMap<Integer, String>();
//Process Start
readMap = dsAdapter.initScan();
//Process End
if (!readMap.isEmpty() && readMap != null) {
isWritten = true;
isDeviceSideEnabled();
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
}
}
}
答
你可以禁用用的方法等所有节点以下,但如果你也想要同时有事情发生使用StackPane
可以彼此叠加等待首选。
public void setNodesDiabled(boolean disable, Node... nodes) {
for(Node node : nodes) {
node.setDisable(disable);
}
}
使用任意节点数,您可以禁用和重新启用与进程相关的尽可能多的节点。它也有助于清理,因为您不会有几个node.setDisable(true); node2.setDisable(true);
等。
在这个例子中,你将不需要0,因为StackPane叠加层可以防止点击除了里面的东西之外的任何东西。背景颜色是70%alpha的灰色,以便您可以告诉它是覆盖层。
public class ProgressExample extends Application {
public StackPane layout, main, progress;
public StackPane createProgressPane() {
ProgressIndicator indicator = new ProgressIndicator();
indicator.setMaxHeight(50);
indicator.setMaxWidth(50);
StackPane pane = new StackPane();
pane.setAlignment(Pos.CENTER);
pane.setStyle("-fx-background-color: rgba(160,160,160,0.7)");
pane.getChildren().add(indicator);
Task<Void> task = new Task<Void>(){
protected Void call() throws Exception {
// Your process here.
// Any changes to UI components must be inside Platform.runLater() or else it will hang.
Thread.sleep(2000);
Platform.runLater(() -> {
layout.getChildren().remove(pane);
});
return null;
}
};
new Thread(task).start();
return pane;
}
public StackPane createMainPane() {
Label label = new Label("Hello World!");
label.setFont(Font.font("Tahoma", FontWeight.SEMI_BOLD, 16));
Button start = new Button("Start Process");
start.setOnAction(action -> {
progress = createProgressPane();
layout.getChildren().add(progress);
});
VBox vbox = new VBox(10);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(label, start);
vbox.setPadding(new Insets(10,10,10,10));
StackPane pane = new StackPane();
pane.getChildren().add(vbox);
return pane;
}
public void start(Stage stage) throws Exception {
main = createMainPane();
layout = new StackPane();
layout.getChildren().add(main);
Scene scene = new Scene(layout, 900, 550);
stage.setScene(scene);
stage.setTitle("Progress Example");
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
我相信问题是,你正在试图改变Task
内的TextField
S中的值是不是FX应用程序线程这就是为什么你越来越Not on FX application thread
。为了解决这个问题,你需要把你的行修改为Platform.runLater()
内的节点,如下所示。
if (readMap != null && !readMap.isEmpty()) { // Swap the order, can't check empty if it's null.
isWritten = true;
isDeviceSideEnabled();
Platform.runLater(() -> {
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
});
}
答
这里是一个SSCE:
它采用了Service可以多次启动。它不完整,但开始。
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Service<Void> serv = new Service<Void>() {
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
int maxWork = 10;
for (int i = 0; i < maxWork; i++) {
Thread.sleep(1000);
updateProgress(i + 1, maxWork);
}
return null;
}
@Override
protected void succeeded() {
super.succeeded();
updateProgress(1, 1);
}
@Override
protected void cancelled() {
super.cancelled();
updateProgress(1, 1);
}
@Override
protected void failed() {
super.failed();
updateProgress(1, 1);
}
};
}
};
ProgressIndicator pi = new ProgressIndicator();
pi.progressProperty().bind(serv.progressProperty());
Button bStart = new Button("Start");
bStart.setOnAction(e -> {
serv.reset();
serv.start();
});
root.setCenter(bStart);
root.setBottom(pi);
primaryStage.setScene(scene);
primaryStage.show();
pi.getScene().getRoot().disableProperty().bind(serv.runningProperty());
}
public static void main(String[] args) {
launch(args);
}
}
在CSS我说:
.progress-indicator:disabled {
-fx-opacity: 1;
}
你能告诉我这是什么Thread.sleep(2000); 我是否需要计算我的方法所需的时间,然后在那段时间睡眠该线程,并且由于项目包含我必须使用BorderPane布局。 我收到以下错误: 线程“线程-5”中的异常java.lang.IllegalStateException:不在FX应用程序线程上; currentThread = Thread-5 –
在这个例子中使用'Thread.sleep(2000);'只是因为它是一个例子。如果没有任何内容需要花费时间,那么进度窗格在测试时不会显示足够长的时间。你不需要它。至于错误,你可以编辑问题在“Task”中包含这些行吗?你需要在你自己的'Platform.runLater()'中放置改变FX节点和窗格的行。 –
Mathews,我添加了包含我的过程的代码。该过程从设备a读取的值显示在UI上。 –