Javafx如何实现忙等待对话例程
Javafx how to implement a busy and wait dialog routine
我遇到了与 , but I use a javafx dialog Alert 中暴露的问题类似的问题,以便使用如下代码在进度条内显示一个对话框:
Alert busyWaitAlert = new Alert(
Alert.AlertType.INFORMATION);
busyWaitAlert.setContentText("Operation in progress");
ButtonType cancelButton = new ButtonType("Cancel", ButtonBar.ButtonData.FINISH);
busyWaitAlert.getButtonTypes().setAll(cancelButton);
busyWaitAlert.setTitle("Running Operation");
busyWaitAlert.setHeaderText("Please wait... ");
ProgressBar progressBar = new ProgressBar();
busyWaitAlert.setGraphic(progressBar);
progressBar.setProgress(ProgressBar.INDETERMINATE_PROGRESS);
Task<Boolean> task;
task = new Task<Boolean>() {
@Override
public Boolean call() throws Exception {
Collections.sort(LnkListImportedToDb, new importDataToDb.DataItemImportedToDbTimeCmp1());
return true;
}
};
task.setOnSucceeded((e) -> {
busyWaitAlert.close();
});
progressBar.progressProperty().bind(task.progressProperty());
busyWaitAlert.contentTextProperty().bind(task.messageProperty());
new Thread(task).start();
问题是在一个任务中,我调用了一个 Collection.Sort,我不知道它的持续时间,而且我无法在 "Task call" 方法中执行 updateMessage,没有这个警告对话框冻结。
我想通过在第一个任务的调用中插入另一个管理 Collection.sort 的任务来解决问题,有没有更好的解决方案?
Task
或更笼统地说 Worker
的目的是 运行 在后台线程上进行长时间的 运行ning 进程,同时能够更新用户至于它的进展。 Task
class 为您提供了许多允许与 JavaFX 应用程序线程安全通信的方法,例如 updateProgress
、updateMessage
和 setOnSucceeded
。但是,您 不需要 使用这些功能来使用 Task
;并且不使用这些功能不会以任何方式在功能上损害 Task
。
例如,您可以有一个非常简单的 Task
,如下所示:
public class TaskImpl<E> extends Task<Void> {
private final List<E> list;
public TaskImpl(List<E> list) { this.list = list; }
@Override protected Void call() throws Exception {
list.sort(null);
return null;
}
}
在这里,代码没有利用 Task
,只是用于在后台线程上对 List
进行排序。外部代码仍然可以观察到 Task
而没有任何不良影响。例如,您的 Alert
不会冻结,因为 message
和 progress
属性未更新。
但是,如果您不打算使用 Task
的功能,您也可以只使用普通的旧 Runnable
或 Callable
。以上在功能上等同于:
() -> list.sort(null);
Runnable
和 Task
(实现 Runnable
)有一些共同点:两者都没有在单独的线程上处理 运行ning 自身你。您实际上必须将工作传递给另一个线程。您可以通过直接创建 Thread
或更好的选择,使用 java.util.concurrent
包中提供的 Executor
框架来做到这一点。
综上所述,我不确定我是否理解您遇到的问题。由于您不知道 List.sort
的调用需要多长时间 Task
的 progress
属性 应该只是保持不确定。如果你想显示一条消息,你可以这样做:
@Override protected Void call() {
updateMessage("Sorting...");
list.sort(null);
updateMessage("Sorting complete.");
updateProgress(1, 1); // jumps progress from indeterminate to 100%
return null;
}
然后将适当的控件绑定到适当的属性,将控件添加到 Alert
,然后显示 Alert
。这是一个关于如何做到这一点的完整工作示例:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main extends Application {
private List<Integer> integerList;
@Override
public void init() {
/*
* If there is at least one parameter attempt to parse the
* first one as an Integer. This value will be used as the
* size of the randomly generated "integerList".
*
* If there are no parameters then default to 10 million.
*/
List<String> params = getParameters().getRaw();
if (!params.isEmpty()) {
generateRandomList(Integer.parseInt(params.get(0)));
} else {
generateRandomList(10_000_000);
}
}
private void generateRandomList(int size) {
System.out.printf("Generating random list of %,d integers...%n", size);
Random r = new Random();
integerList = Stream.generate(() -> r.nextInt(size))
.limit(size)
.collect(Collectors.toList());
System.out.println("List generated.");
}
@Override
public void start(Stage primaryStage) {
System.out.println("Building scene graph...");
ListView<Integer> listView = new ListView<>();
listView.setPrefWidth(500);
listView.getItems().addAll(integerList);
Button btn = new Button("Sort List");
btn.setOnAction(ae -> {
ae.consume();
btn.setDisable(true);
/*
* Here we create the task and configure it to set items of the listView
* to the result. We do this because we are sorting "integerList" which
* we had *copied* into the ListView. If we sorted the items of the ListView
* then we would be altering the UI from a background thread (big no-no!).
* Therefore we need to re-copy the "integerList" into the ListView upon
* completion of the task.
*/
SortingTask<Integer> task = new SortingTask<>(integerList, null);
task.setOnSucceeded(wse -> listView.getItems().setAll(task.getValue()));
Alert alert = createProgressAlert(primaryStage, task);
executeTask(task);
alert.show();
});
VBox root = new VBox(btn, listView);
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
VBox.setVgrow(listView, Priority.ALWAYS);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Progress Alert Example");
primaryStage.setResizable(false);
primaryStage.show();
System.out.println("Primary stage displayed.");
}
// executes task on a separate, daemon thread
private void executeTask(Task<?> task) {
Thread t = new Thread(task, "sorting-thread");
t.setDaemon(true);
t.start();
}
// creates the Alert and necessary controls to observe the task
private Alert createProgressAlert(Stage owner, Task<?> task) {
Alert alert = new Alert(Alert.AlertType.NONE);
alert.initOwner(owner);
alert.titleProperty().bind(task.titleProperty());
alert.contentTextProperty().bind(task.messageProperty());
ProgressIndicator pIndicator = new ProgressIndicator();
pIndicator.progressProperty().bind(task.progressProperty());
alert.setGraphic(pIndicator);
alert.getDialogPane().getButtonTypes().add(ButtonType.OK);
alert.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty().bind(task.runningProperty());
alert.getDialogPane().cursorProperty().bind(
Bindings.when(task.runningProperty())
.then(Cursor.WAIT)
.otherwise(Cursor.DEFAULT)
);
return alert;
}
// The Task implementation
private static class SortingTask<E> extends Task<List<E>> {
private final List<E> list;
private final Comparator<? super E> comparator;
private SortingTask(List<E> list, Comparator<? super E> comparator) {
this.list = Objects.requireNonNull(list);
this.comparator = comparator; // if null then natural order is used by List.sort
updateTitle("Sorting Task");
}
@Override
protected List<E> call() throws Exception {
updateMessage("Sorting list...");
list.sort(comparator);
updateMessage("Sorting complete.");
updateProgress(1, 1);
return list;
}
@Override
protected void running() {
System.out.println("Sorting task is running...");
}
@Override
protected void succeeded() {
System.out.println("Sorting task successful.");
}
}
}
如果您希望自定义随机生成的列表的大小,您可以将一个整数作为参数传递给该程序。如果传递了多个参数,则忽略第一个参数。如果没有给出参数,则大小默认为 10,000,000(一千万)。根据您的计算机,您可能希望增加或减少列表的大小,以便 Task
不会完成得太快(或花费太长时间)。
我遇到了与
Alert busyWaitAlert = new Alert(
Alert.AlertType.INFORMATION);
busyWaitAlert.setContentText("Operation in progress");
ButtonType cancelButton = new ButtonType("Cancel", ButtonBar.ButtonData.FINISH);
busyWaitAlert.getButtonTypes().setAll(cancelButton);
busyWaitAlert.setTitle("Running Operation");
busyWaitAlert.setHeaderText("Please wait... ");
ProgressBar progressBar = new ProgressBar();
busyWaitAlert.setGraphic(progressBar);
progressBar.setProgress(ProgressBar.INDETERMINATE_PROGRESS);
Task<Boolean> task;
task = new Task<Boolean>() {
@Override
public Boolean call() throws Exception {
Collections.sort(LnkListImportedToDb, new importDataToDb.DataItemImportedToDbTimeCmp1());
return true;
}
};
task.setOnSucceeded((e) -> {
busyWaitAlert.close();
});
progressBar.progressProperty().bind(task.progressProperty());
busyWaitAlert.contentTextProperty().bind(task.messageProperty());
new Thread(task).start();
问题是在一个任务中,我调用了一个 Collection.Sort,我不知道它的持续时间,而且我无法在 "Task call" 方法中执行 updateMessage,没有这个警告对话框冻结。 我想通过在第一个任务的调用中插入另一个管理 Collection.sort 的任务来解决问题,有没有更好的解决方案?
Task
或更笼统地说 Worker
的目的是 运行 在后台线程上进行长时间的 运行ning 进程,同时能够更新用户至于它的进展。 Task
class 为您提供了许多允许与 JavaFX 应用程序线程安全通信的方法,例如 updateProgress
、updateMessage
和 setOnSucceeded
。但是,您 不需要 使用这些功能来使用 Task
;并且不使用这些功能不会以任何方式在功能上损害 Task
。
例如,您可以有一个非常简单的 Task
,如下所示:
public class TaskImpl<E> extends Task<Void> {
private final List<E> list;
public TaskImpl(List<E> list) { this.list = list; }
@Override protected Void call() throws Exception {
list.sort(null);
return null;
}
}
在这里,代码没有利用 Task
,只是用于在后台线程上对 List
进行排序。外部代码仍然可以观察到 Task
而没有任何不良影响。例如,您的 Alert
不会冻结,因为 message
和 progress
属性未更新。
但是,如果您不打算使用 Task
的功能,您也可以只使用普通的旧 Runnable
或 Callable
。以上在功能上等同于:
() -> list.sort(null);
Runnable
和 Task
(实现 Runnable
)有一些共同点:两者都没有在单独的线程上处理 运行ning 自身你。您实际上必须将工作传递给另一个线程。您可以通过直接创建 Thread
或更好的选择,使用 java.util.concurrent
包中提供的 Executor
框架来做到这一点。
综上所述,我不确定我是否理解您遇到的问题。由于您不知道 List.sort
的调用需要多长时间 Task
的 progress
属性 应该只是保持不确定。如果你想显示一条消息,你可以这样做:
@Override protected Void call() {
updateMessage("Sorting...");
list.sort(null);
updateMessage("Sorting complete.");
updateProgress(1, 1); // jumps progress from indeterminate to 100%
return null;
}
然后将适当的控件绑定到适当的属性,将控件添加到 Alert
,然后显示 Alert
。这是一个关于如何做到这一点的完整工作示例:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main extends Application {
private List<Integer> integerList;
@Override
public void init() {
/*
* If there is at least one parameter attempt to parse the
* first one as an Integer. This value will be used as the
* size of the randomly generated "integerList".
*
* If there are no parameters then default to 10 million.
*/
List<String> params = getParameters().getRaw();
if (!params.isEmpty()) {
generateRandomList(Integer.parseInt(params.get(0)));
} else {
generateRandomList(10_000_000);
}
}
private void generateRandomList(int size) {
System.out.printf("Generating random list of %,d integers...%n", size);
Random r = new Random();
integerList = Stream.generate(() -> r.nextInt(size))
.limit(size)
.collect(Collectors.toList());
System.out.println("List generated.");
}
@Override
public void start(Stage primaryStage) {
System.out.println("Building scene graph...");
ListView<Integer> listView = new ListView<>();
listView.setPrefWidth(500);
listView.getItems().addAll(integerList);
Button btn = new Button("Sort List");
btn.setOnAction(ae -> {
ae.consume();
btn.setDisable(true);
/*
* Here we create the task and configure it to set items of the listView
* to the result. We do this because we are sorting "integerList" which
* we had *copied* into the ListView. If we sorted the items of the ListView
* then we would be altering the UI from a background thread (big no-no!).
* Therefore we need to re-copy the "integerList" into the ListView upon
* completion of the task.
*/
SortingTask<Integer> task = new SortingTask<>(integerList, null);
task.setOnSucceeded(wse -> listView.getItems().setAll(task.getValue()));
Alert alert = createProgressAlert(primaryStage, task);
executeTask(task);
alert.show();
});
VBox root = new VBox(btn, listView);
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
VBox.setVgrow(listView, Priority.ALWAYS);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Progress Alert Example");
primaryStage.setResizable(false);
primaryStage.show();
System.out.println("Primary stage displayed.");
}
// executes task on a separate, daemon thread
private void executeTask(Task<?> task) {
Thread t = new Thread(task, "sorting-thread");
t.setDaemon(true);
t.start();
}
// creates the Alert and necessary controls to observe the task
private Alert createProgressAlert(Stage owner, Task<?> task) {
Alert alert = new Alert(Alert.AlertType.NONE);
alert.initOwner(owner);
alert.titleProperty().bind(task.titleProperty());
alert.contentTextProperty().bind(task.messageProperty());
ProgressIndicator pIndicator = new ProgressIndicator();
pIndicator.progressProperty().bind(task.progressProperty());
alert.setGraphic(pIndicator);
alert.getDialogPane().getButtonTypes().add(ButtonType.OK);
alert.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty().bind(task.runningProperty());
alert.getDialogPane().cursorProperty().bind(
Bindings.when(task.runningProperty())
.then(Cursor.WAIT)
.otherwise(Cursor.DEFAULT)
);
return alert;
}
// The Task implementation
private static class SortingTask<E> extends Task<List<E>> {
private final List<E> list;
private final Comparator<? super E> comparator;
private SortingTask(List<E> list, Comparator<? super E> comparator) {
this.list = Objects.requireNonNull(list);
this.comparator = comparator; // if null then natural order is used by List.sort
updateTitle("Sorting Task");
}
@Override
protected List<E> call() throws Exception {
updateMessage("Sorting list...");
list.sort(comparator);
updateMessage("Sorting complete.");
updateProgress(1, 1);
return list;
}
@Override
protected void running() {
System.out.println("Sorting task is running...");
}
@Override
protected void succeeded() {
System.out.println("Sorting task successful.");
}
}
}
如果您希望自定义随机生成的列表的大小,您可以将一个整数作为参数传递给该程序。如果传递了多个参数,则忽略第一个参数。如果没有给出参数,则大小默认为 10,000,000(一千万)。根据您的计算机,您可能希望增加或减少列表的大小,以便 Task
不会完成得太快(或花费太长时间)。