【问题标题】:Progress Indicator won't display until main code stops在主代码停止之前,进度指示器不会显示
【发布时间】:2016-03-19 05:58:46
【问题描述】:

我已经为一个愚蠢的问题苦苦挣扎了好几天,我需要你的帮助。我只是试图在一个单独的阶段中显示一个不确定的进程指示器,如下所示,而我的主代码执行一个循环。如果在循环中满足某个条件,则进度阶段应该关闭,主代码继续。

现在我可以在主代码开始循环之前打开一个新阶段,但进度指示器不会显示在阶段中。但是一旦条件满足,那么指示器就会突然出现在舞台上并旋转。但是一旦主代码再次开始运行,指标就会冻结,但仍然可见。

我将简要解释下面每个代码的作用。如何使加载阶段最初出现在指示器可见的情况下,然后在调用 showMessage() 方法时关闭该阶段?基本上我想向用户展示后台循环正在发生,直到主代码到达showMessage()

Main.java 我不会发布它,因为它只会创建第一个 GUI。它使用menu.fxml 作为FXMLLoader 中的资源...而menu.fxml 使用Controller.java 作为控制器。

Controller.java 此代码将主 GUI 中的场景更改为具有新布局,要求用户单击可见按钮之一。然后它检查源按钮是否为Button1...,如果是,则创建/显示一个使用sample.fxml 作为资源的新阶段。然后运行最终文件Loop.java

public class Controller implements Initializable {

    public Stage loadingStage;

    @FXML
    ProgressIndicator progressIndicator;

    public void handlerButtonClick() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("field.fxml"));
        Parent rootOne = loader.load();
        ((Controller) loader.getController()).setPrimaryStage(primaryStage);
        ((Controller) loader.getController()).setPrimaryScene(scene);
        sceneField = new Scene(rootOne, 420, 510);
        primaryStage.setScene(sceneField);
        primaryStage.setTitle("Import Data");
        primaryStage.show();
    }

    public void fieldOption(ActionEvent e) throws Exception {

        source = e.getSource();
        if (source == Button1) {

            Loop loopObj = new Main();
            String[] args = {};

            //Create new stage to contain the Progress Indicator
            FXMLLoader loader2 = new FXMLLoader(getClass().getResource("sample.fxml"));
            Parent root2 = loader2.load();
            Controller2 controller = loader2.getController();
            loadingStage = new Stage();
            loadingStage.setTitle("Loading Stage");
            Scene scene = new Scene(root2, 200, 200);
            loadingStage.setScene(scene);
            loadingStage.show();

            //Runs the looper
            loopObj.start(stage);
        }

    }

Sample.fxml

此代码创建进度指示器,并使用控制器Controller2

<AnchorPane prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml/1"
            xmlns="http://javafx.com/javafx/2.2" fx:controller="Controller2">
    <children>
        <ProgressIndicator fx:id="progressIndicator" layoutX="78.0" layoutY="55.0" progress="-1.0"/>
    </children>
</AnchorPane>

Controller2.java 此代码实现Initializable 以使进度指示器可见:

public class Controller2 implements Initializable {
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        ProgressIndicator progressIndicator = new ProgressIndicator();
        progressIndicator.setVisible(true);
    }
}

Loop.java 这是执行循环的主要代码。加载阶段应显示进度指示器直到 Loop.java 调用其showMessage() 函数,此时加载阶段关闭。我知道其中一种方法似乎不是必需的,但这只是因为它们被剥离用于演示目的。

public class Loop extends Application {
    @Override
    public void start(Stage ignored) throws Exception {
        Platform.setImplicitExit(false);
        for (int i = 0; i <= 100; i++) {
            if (i == 75){
                saveAttachment("Hello");
            }
        }
    }

    public static void saveAttachment(String subject) throws IOException, MessagingException {
        showMessage(subject);
    }

    private static void showMessage(String subject) throws IOException, MessagingException {
        // Close the loading stage here.
    }
}

注意它如何使用public void start(Stage ignored)。 Main.java 使用public void start(Stage primaryStage)。也许这很糟糕。 我很沮丧,请帮忙!

更新

我已经采纳了 Brian 的建议,并在 Controller.java 中添加了一个新任务,如下所示:

 Stage loadingStage = new Stage();

           //create task object
           Task<Void> task = new Task<Void>(){
               @Override
               protected Void call() throws Exception{
                   System.out.println("Background task started...");
                   FXMLLoader loader2 = new FXMLLoader(getClass().getResource("sample.fxml"));
                   Parent root2 = loader2.load();
                   Controller2 controller = loader2.getController();
                   loadingStage.setTitle("Hello World");
                     Scene scene = new Scene(root2, 450, 250);
                   System.out.println("HERE6");
                   loadingStage.setScene(scene);
                   System.out.println("HERE7");
                   loadingStage.show();
                   System.out.println("HERE8");
                   return null;
               }
           };

           Thread th = new Thread(task);
           th.setDaemon(true);
           System.out.println("Starting background task...");
           th.start();

现在的问题是它没有到​​达显示“HERE7”的打印输出行。它成功进入任务并继续执行主代码,但任务没有超出打印“HERE6”的位置,因此没有打开新阶段。什么给了?

【问题讨论】:

  • 您无法在后台线程上更新 UI,因此它会在 loadingStage.setScene(...) 处引发异常。但我不明白你为什么要在后台线程中做那部分。肯定是for 循环以及对saveAttachment 的所有调用需要很多时间吗?也许阅读我对stackoverflow.com/questions/30249493/… 的回答以获取一些背景信息。 Task API docs 充满了例子。
  • 谢谢,我目前正在修改和阅读。会回来报告的。
  • 我已经到了可以正确打开舞台的地步,但我了解到为此需要FutureTask
  • 为什么需要FutureTask
  • 我创建的任务线程涉及一个for 循环,其中包含对另一个方法的调用。这种其他方法会显示一个对话框,应该在继续之前等待用户输入。显然,后台线程需要特殊处理才能暂停,futureTask 通过使用其get 方法允许这样做。正确的?我很困惑如何正确地将当前驻留在我的Task 中的大型代码封装到FutureTask 中。我的问题有相当大的切线,所以我觉得创建一个新的、干净的问题是合理的。欢迎所有反馈。

标签: java javafx concurrency task progress-indicator


【解决方案1】:

在 Ctlr2 中你有 ProgressIndicator progressIndicator = new ProgressIndicator(); 但如果它在 FXML 文件中你不创建一个新的。出于某种原因,就像你在第一个 Ctlr 中那样做@FXML ProgressIndicator progressIndicator;??

但这不会解决您的问题,您在 JavaFX 应用程序线程(GUI 线程)上运行所有内容。可能发生的事情(我并没有真正阅读代码)是你在同一个线程上做这两件事,进度指示器必须等待 main 完成,然后它才能更新 GUI。您为任务和并发添加标签,这就是您所需要的。

查看此处并搜索更多示例。 https://stackoverflow.com/a/22847734/2855515

【讨论】:

  • 谢谢布赖恩,我现在走在正确的轨道上。我已经更新了我的问题(见最底部)。它成功进入任务,但不会创建或显示新阶段。它只是在编写“后台任务启动”后挂起,而主代码继续。怎么了?
  • 实际上,它不会超出它所说的“HERE6”。出于某种原因,loadingStage.setScene(scene); 行导致它停止。
猜你喜欢
  • 1970-01-01
  • 2012-10-15
  • 2012-09-10
  • 1970-01-01
  • 1970-01-01
  • 2011-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多