Javafx如何实现忙和等待对话框例程

时间:2018-07-17 17:45:23

标签: java sorting javafx progress-bar javafx-8

我有一个类似JavaFX indeterminate progress bar while doing a process的问题,但是我使用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。排序的过程我不知道持续时间,并且无法在“任务调用”方法内执行updateMessage,否则警报对话框将冻结。 我想通过在第一个Task的调用中插入另一个管理Collection.sort的Task来解决该问题,是否有更好的解决方案?

1 个答案:

答案 0 :(得分:2)

Task(或更常见的是Worker)的目的是在后台线程上运行长时间运行的进程,同时能够向用户更新其进度。 Task类为您提供了许多允许与JavaFX Application Thread安全通信的方法,例如updateProgressupdateMessagesetOnSucceeded。但是,您不需要使用这些功能来使用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不会冻结,因为messageprogress属性没有被更新。

但是,如果您不打算使用Task的功能,也可以只使用普通的旧RunnableCallable。上面的功能等效于:

() -> list.sort(null);

RunnableTask(实现Runnable)都有一个共同点:没有人照顾自己在单独的线程上运行。您实际上必须将工作传递给另一个线程。您可以通过直接创建Thread或使用Executor软件包中提供的java.util.concurrent框架(更好的选择)来完成此操作。

话虽如此,我不确定我是否理解您遇到的问题。由于您不知道调用List.sort将花费Task的{​​{1}}属性多长时间,因此应该保持不确定性。如果要显示一条消息,可以执行以下操作:

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

如果要自定义随机生成的列表的大小,可以将整数作为参数传递给该程序。如果传递了多个参数,则除第一个以外的所有参数都将被忽略。如果未提供任何参数,则大小默认为10,000,000(1000万)。根据您的计算机,您可能需要增加或减小列表的大小,以使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."); } } } 的完成时间不会太快(或花费太长时间)。