java JAVAFX 如何更新 GUI 元素(节点)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36992145/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:05:28  来源:igfitidea点击:

JAVAFX how to update GUI elements (nodes)

javamultithreadinguser-interfacejavafx

提问by AnonymousGuy

This question is already asked but i copuldnt udnerstand it. Imagine that. I have a program with 2 scenes. First scene1 is opened which is the scene for connection to database. There is a label called Status which should turn from "Disconnected" to "Connected" when the connection is established(by clicking the COnnect button). So i made a function to take the button "Connect" onClick event. This function is declared and defined inside a controller class (i am using fmxl designing with scene builder). So basicly i want to change the status to "COnnected" (status.setText("Connected")) from the connection function(method) which is inside the controller class. However when I do that, the text isn't changed instantly after the connection is established, but it changes when the scene is about to close and i am about to change the scene to the new one... I read on the internet and i saw that i should use Platform.runLater and threading so i tried:

这个问题已经有人问过了,但我不明白。想象一下。我有一个包含 2 个场景的程序。打开第一个scene1,这是连接数据库的场景。有一个名为 Status 的标签,在建立连接时应从“Disconnected”变为“Connected”(通过单击 COnnect 按钮)。所以我做了一个函数来获取按钮“连接”onClick 事件。这个函数是在控制器类中声明和定义的(我使用 fmxl 设计和场景构建器)。所以基本上我想从控制器类内部的连接函数(方法)中将状态更改为“已连接”(status.setText(“已连接”))。但是,当我这样做时,建立连接后文本不会立即更改,

private void changeSC() throws IOException, InterruptedException, SQLException
{

    dbConnect();

    Thread thrd = new Thread() {

        public void run() {

                 Platform.runLater(new Runnable() {
                     @Override public void run() {
                     status.setText("Connected");
                        status.setTextFill(Color.GREEN);
                     }});
        }
    };
    thrd.start();

    //pb.setProgress(1.0);
    Parent root = FXMLLoader.load(getClass().getResource("Design.fxml"));
    Scene primary = new Scene(root,1024,768);
    primary.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
    System.out.println("Text changing to COnnected");
    status.setTextFill(Color.GREEN);
    Thread.sleep(2000);
    Main.window.setScene(primary);

}

changeSC is the function that is executed when Connect button is clicked. This is my old version which also doesnt work:

changeSC 是单击 Connect 按钮时执行的函数。这是我的旧版本,它也不起作用:

private void changeSC() throws IOException, InterruptedException, SQLException
{

    dbConnect();

      status.setText("Connected");
      status.setTextFill(Color.GREEN);


    //pb.setProgress(1.0);
    Parent root = FXMLLoader.load(getClass().getResource("Design.fxml"));
    Scene primary = new Scene(root,1024,768);
    primary.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
    System.out.println("Text changing to COnnected");
    status.setTextFill(Color.GREEN);
    Thread.sleep(2000);
    Main.window.setScene(primary);

}

The problem is with the text which should change to "Connected". It changes just when my scene is about to be switched....

问题在于应该更改为“已连接”的文本。就在我的场景即将切换时它发生了变化......

回答by jns

You need to use a Task, if you have some long running operation. Otherwise when that operation is called from the JavaFX Application Thread it would block the GUI.

Task如果您有一些长时间运行的操作,则需要使用, 。否则,当从 JavaFX 应用程序线程调用该操作时,它会阻塞 GUI。

If you want to update the GUI from the Taskyou have to use Platform.runlater, which will run the code on the JavaFX Application Thread: Platform.runlater

如果您想从Task您必须使用的更新 GUI Platform.runlater,它将在 JavaFX 应用程序线程上运行代码: Platform.runlater

Updates to the Nodesof your GUI have always to be performed on the JavaFx Thread.

NodesGUI 的更新总是在 JavaFx 线程上执行。

When you update statusinside the Listenerof buttonit should work.

当你status在里面更新Listenerbutton它应该可以工作。

  button.setOnAction(evt -> {
        dbConnect();
        status.setText("Connected");
        // ...
    });

If dbConnect()takes some time, you can use a Task:

如果dbConnect()需要一些时间,您可以使用Task

 Task<Void> longRunningTask = new Task<Void>() {

        @Override
        protected Void call() throws Exception {
            dbConnect();
            Platform.runLater(() -> status.setText("Connected"));
            return null;
        }
    };


    button.setOnAction(e -> {
        new Thread(longRunningTask).start();
    });

回答by leobelizquierdo

Since you are connecting to a database, you should put this code to run in background using Taskor a Serviceto keep the GUI Thread responding to the user inputs. Just remember that only in the GUI Thread you can update the view state (changing the value of a text in your case). You can use a java Threadand use Platform.runLaterwhich means that the code inside is schedule to be precessed by the GUI Thread but in your case you are using in the wrong way. First the logic to connect to the database should be inside the method runof the thread and once the method finish, set the value of the text and do whatever you want after. Also you'll want to show the new Scenewhen all the process has been finished to get a chance to the user to see the change in the text. You can change your code in this way:

由于您要连接到数据库,您应该将此代码在后台运行,使用Task或 aService以保持 GUI 线程响应用户输入。请记住,只有在 GUI 线程中,您才能更新视图状态(在您的情况下更改文本的值)。您可以使用 javaThread和 usePlatform.runLater这意味着里面的代码计划由 GUI 线程处理,但在您的情况下,您以错误的方式使用。首先连接数据库的逻辑应该run在线程的方法内部,一旦方法完成,设置文本的值,然后做任何你想做的事情。此外,您还需要Scene在所有过程完成后显示新内容,以便用户有机会看到文本中的更改。您可以通过以下方式更改代码:

private void changeSC() throws IOException, InterruptedException, SQLException
{
  Thread thrd = new Thread() {

    public void run() {
        dbConnect();
        Platform.runLater(new Runnable() {
             @Override public void run() {
                 status.setText("Connected");
                 status.setTextFill(Color.GREEN);
                 //pb.setProgress(1.0);
                 Parent root = FXMLLoader.load(getClass().getResource("Design.fxml"));
                 Scene primary = new Scene(root,1024,768);
                 primary.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
                 System.out.println("Text changing to COnnected");
                 status.setTextFill(Color.GREEN);
                 Main.window.setScene(primary);
              }
        });
     }
  };
  thrd.start();
}

If you choose use a Taskyou dont have to deal with Platform.runLaterexplicitly. You only need to create a task (an implementation of the class Task), wrap this inside a java Thread, start it and the set a handler for the different events (eg: setOnSucceeded). This is your code using Task:

如果您选择使用 aTask您不必Platform.runLater明确处理。您只需要创建一个任务(类的实现Task),将其包装在一个 java 中Thread,启动它并为不同的事件设置一个处理程序(例如:)setOnSucceeded。这是您使用的代码Task

private void changeSC() throws IOException, InterruptedException, SQLException
{  
    Task<Void> task = new Task<Void>(){
        @Overrdie
        protected Void call()  {
            dbConnect();   
            return null;
        }
    };
    //start Task
    Thread t = new Thread(task);
    t.setDaemon(true); // thread will not prevent application shutdown
    t.start();

    task.setOnSucceeded(event -> {
        Parent root = FXMLLoader.load(getClass().getResource("Design.fxml"));
        Scene primary = new Scene(root,1024,768);
        primary.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        System.out.println("Text changing to COnnected");
        status.setTextFill(Color.GREEN);
        Main.window.setScene(primary);
    });


}

回答by AnonymousGuy

ok i fixed it by setting task.SetonFailed() :)

好的,我通过设置 task.SetonFailed() 来修复它:)