我有多个任务在一个线程中运行。这很有效,相应的tablecell进度条会按预期更新。在我执行它们之前,我想要禁用UI上的所有按钮(这是我的enableControls
方法)。当最后一个任务运行时,我重新启用按钮。
我尝试了以下操作,添加了关闭和awaitTermination
但是除非完成所有操作,否则UI不会更新。
ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
enableControls(false);
for (Segment seg : selectedItems) {
Task loadingTask = new Task<Void>() {
@Override
public Void call() {
for (int i = 1; i <= 100; i++) {
updateProgress(i, 100);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
};
seg.progressBarProperty().bind(loadingTask.progressProperty());
loadingTask.setOnSucceeded(e -> seg.progressBarProperty().unbind());
loadingTask.setOnFailed(e -> {
loadingTask.getException().printStackTrace();
enableControls(true);
});
exec.submit(loadingTask);
}
exec.shutdown();
try {
exec.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test");
enableControls(true);
或者我想到确定当前任务是否是最后一个(循环中不再有seg)并重新启用setOnSucceeded
中的UI控件。至少陈述并不优雅。
任何想法为什么UI(通过绑定值)没有更新 - 这是awaitTermination
的限制吗?
答案 0 :(得分:1)
awaitTermination是一个阻塞调用,你不应该在JavaFX应用程序线程上调用它。只需创建一个新任务来管理任务执行并等待其他任务的终止。您可以对新任务使用绑定,以在UI运行时禁用部分UI。
import javafx.application.*;
import javafx.concurrent.Task;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class Waiter extends Application {
private static final int N_TASKS = 3;
private static final int TOTAL_TASK_EXECUTION_TIME_LIMIT_SECS = 20;
private final VBox layout = new VBox(10);
@Override
public void start(Stage stage) throws Exception {
Button run = new Button("Run");
run.setOnAction(this::work);
layout.getChildren().add(run);
layout.setPadding(new Insets(10));
layout.setPrefSize(100, 150);
stage.setScene(new Scene(layout));
stage.show();
}
private void work(Event event) {
Node controlNode = (Node) event.getSource();
ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "load");
t.setDaemon(true);
return t;
});
ExecutorService monitor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "monitor");
t.setDaemon(true);
return t;
});
final List<LoadingTask> tasks = new ArrayList<>();
for (int j = 0; j < N_TASKS; j++) {
LoadingTask loadingTask = new LoadingTask();
ProgressBar progressBar = new ProgressBar();
progressBar.progressProperty().bind(loadingTask.progressProperty());
layout.getChildren().add(progressBar);
tasks.add(loadingTask);
}
Task<Void> taskRunner = new Task<Void>() {
@Override
protected Void call() throws Exception {
tasks.forEach(exec::submit);
exec.shutdown();
boolean cleanShutdown = exec.awaitTermination(
TOTAL_TASK_EXECUTION_TIME_LIMIT_SECS,
TimeUnit.SECONDS
);
if (!cleanShutdown) {
exec.shutdownNow();
}
Platform.runLater(() ->
layout.getChildren()
.removeIf(
node -> node instanceof ProgressBar
)
);
return null;
}
};
controlNode.disableProperty().bind(taskRunner.runningProperty());
monitor.submit(taskRunner);
monitor.shutdown();
}
public static void main(String[] args) {
launch(args);
}
private static class LoadingTask extends Task<Void> {
@Override
public Void call() {
for (int i = 1; i <= 100; i++) {
if (isCancelled() || Thread.interrupted()) {
break;
}
updateProgress(i, 100);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return null;
}
}
}
我需要禁用舞台上的所有按钮和文本字段
如果禁用父节点,它将递归禁用父节点的所有子节点。因此禁用场景中的所有内容,只需绑定场景根的disableProperty:
controlNode.getScene().getRoot()
.disableProperty().bind(taskRunner.runningProperty());
在父级上禁用输入的另一种方法是使用具有所有者集的WINDOW_MODAL或APPLICATION_MODAL对话框。例如,ControlsFX ProgressDialog。