【问题标题】:Update JavaFX 8 UI / Multithread更新 JavaFX 8 UI/多线程
【发布时间】:2015-10-26 16:13:48
【问题描述】:

现在我尝试更新 Javafx 用户界面,同时自动发送 2 个或更多电子邮件。 UI 应该使用“connecting”、“sending”和“sent”来更新标签。我已经阅读了有关 Runnables、Tasks、TimeLine 的信息,但我真的不明白我应该使用哪种方法以及它是如何工作的。

在我读到关于 Platform.runLater() 的所有地方,我都使用了它,但我的 JavaFX Gui 中的动画会冻结,并且标签只会在所有电子邮件都发送后发生变化。

我很快就会用我的代码更新这篇文章,但是有人可以告诉我应该使用哪种线程以及如何轻松更新 ui 吗?

//编辑

感谢您的帮助,Branislav,但现在我收到“不在 FX 应用程序线程”-错误。以下代码不是我的“扩展应用程序”类,请看一下。

public class ControllerOCM implements Initializable{



    @FXML
    private ComboBox<String> comboEmpf;

    @FXML
    private Label lblStatus;

    @FXML
    private Label lblStatus1;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        hint.setText("some hint");
    }

    public void sende(ActionEvent event) throws Exception {

        switch (comboEmpf.getValue()) {
        case "A": System.out.println("A");break;
        case "B":
        File fTEST = new File("C:\\B\\");
        File[] fpathTEST = fTEST.listFiles();
        String[] fnameTEST = fTEST.list();

    for (int i=0; i<fpathTEST.length; i++) {
        SendenTask sendTEST = new SendenTask(mail@address.com,
                "bodycontent",
                "subject", 
                fpathTEST[i].toString(),    
                fnameTEST[i],               
                i,                      //count
                fpathTEST.length);      //max

        new Thread(sendTEST).start();
    }
            break;
        }
    }

     public class SendenTask extends Task<Void> {
            private String adr;
            private String body;
            private String subj;
            private String fp;
            private String fn;
            private int count;
            private int max;

         public SendenTask(String adr, String body, String subj, String fp, String fn, int count, int max) {
            this.adr = adr;
            this.body = body;
            this.subj = subj;
            this.fp = fp;
            this.fn = fn;
            this.count = count;
            this.max = max;

         }

         @Override 
         protected Void call() throws Exception {

         lblStatus1.setText("connecting");
         //doing connectionAction

         lblStatus1.setText("sending");
         updateMessage("sending");
         //doing sendingthingys


         //sending complete
         lblStatus1.setText("sent");
         System.out.println("done");
         updateMessage("done");

             return null;
         }
     }
}

//编辑2

现在程序正在一个接一个地发送电子邮件,但 textproperty 无法正常工作。它不刷新文本,它只是清空标签。如果我删除了 unbind-line(成功),它只会在我运行发送任务时起作用。之后,对于所有其他发送任务,它再次为空。

public class ControllerOCM implements Initializable{



        @FXML
        private ComboBox<String> comboEmpf;

        @FXML
        private Label lblStatus;

        @FXML
        private Label lblStatus1;


        @Override
        public void initialize(URL location, ResourceBundle resources) {
            hint.setText("some hint");
        }

        public void sende(ActionEvent event) throws Exception {

            switch (comboEmpf.getValue()) {
            case "A": System.out.println("A");break;
            case "B":
            File fTEST = new File("C:\\B\\");
            File[] fpathTEST = fTEST.listFiles();
            String[] fnameTEST = fTEST.list();

        for (int i=0; i<fpathTEST.length; i++) {
            SendenTask sendTEST = new SendenTask(mail@address.com,
                    "bodycontent",
                    "subject", 
                    fpathTEST[i].toString(),    
                    fnameTEST[i],               
                    i,                      //count
                    fpathTEST.length);      //max

              lblStatus1.textProperty().bind(sendTEST.messageProperty());
              thread = new Thread(sendTEST);
              thread.start();   
              }
              break;
            }
        }

         public class SendenTask extends Task<Void> {
                private String adr;
                private String body;
                private String subj;
                private String fp;
                private String fn;
                private int count;
                private int max;

             public SendenTask(String adr, String body, String subj, String fp, String fn, int count, int max) {
                this.adr = adr;
                this.body = body;
                this.subj = subj;
                this.fp = fp;
                this.fn = fn;
                this.count = count;
                this.max = max;

             }

             @Override 
             protected Void call() throws Exception {

             while(!sending) {
             sending = true

             updateMessage("connecting");
             //doing connectionAction


             updateMessage("sending");
             //doing sendingthingys


             //sending complete
             updateMessage("done");
                 }
                 return null;
             }
     @Override
     protected void succeeded() {
         super.succeeded();
         lblStatus1.textProperty().unbind();
         sending = false;
         }
       }

    }

//edit3 最大的问题是,我该把 lblStatus1.textProperty().bind(sendTEST.messageProperty()); 以及在哪里 lblStatus1.textProperty().unbind(); ?

【问题讨论】:

标签: java multithreading user-interface javafx


【解决方案1】:

你必须使用Task 类。

这是一个小演示。让我们通过for循环模拟耗时任务:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;


public class TaskDemo extends Application {


    @Override
    public void start(Stage stage) throws Exception {
        final Label label = new Label();

        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                boolean fxApplicationThread = Platform.isFxApplicationThread();
                System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
                // Run your time consuming task here!
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    updateMessage("Time elapsed: " + i);
                }
                return null;
            }

            @Override
            protected void succeeded() {
                boolean fxApplicationThread = Platform.isFxApplicationThread();
                System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
                super.succeeded();
                label.textProperty().unbind();
                label.setText("Task done");
            }
        };
        // Bind messageProperty of task for textProperty of Label
        label.textProperty().bind(task.messageProperty());
        stage.setScene(new Scene(label, 300, 200));
        stage.show();
        new Thread(task).start();

    }
}

事情很简单。您的耗时任务将冻结 UI,直到它完成。通过使用Task 类,您的耗时任务在后台线程上运行,使 JavaFX 线程响应。有时,您可以从call 方法“发布”您的任务结果。 不要在后台线程上更新您的 JavaFX UI(在 call 方法中)。通过调用Platform.isFxApplicationThread(),您可以检查您的代码是否在FXApplicationThread 上运行。如果您的代码未在该线程上运行,请不要从那里更新您的 JavaFX UI!

【讨论】:

  • @Simsala 我说什么“耗时”任务正在运行?
  • 非常感谢您到现在为止的帮助。我只是设法让事情运行起来,对我的标签进行了属性绑定,并且我的动画在发送电子邮件时正在运行,但任务当前同时发送电子邮件,但我想要一个接一个。我尝试了 thread.join() 但如果我使用它,我的 UI 会再次冻结。有什么解决办法吗?
猜你喜欢
  • 2014-08-26
  • 1970-01-01
  • 2014-05-11
  • 1970-01-01
  • 1970-01-01
  • 2017-10-07
  • 2015-08-03
  • 2013-01-18
  • 2012-12-28
相关资源
最近更新 更多