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

4

这个问题已经被提出了,但我无法理解。想象一下。我有一个包含两个场景的程序。首先打开场景1,这是连接到数据库的场景。有一个名为“状态”的标签,当连接建立时(通过点击“连接”按钮),应该从“未连接”变为“已连接”。因此,我编写了一个函数来处理按钮“连接”的onClick事件。这个函数在控制器类中声明和定义(我正在使用带有场景构建器的fmxl设计)。所以基本上,我想从控制器类内部的连接函数(方法)更改状态为“已连接”(status.setText(“Connected”))。然而,当我这样做时,文本不会在连接建立后立即更改,而是在场景即将关闭并且我即将切换到新场景时才更改...我在互联网上阅读并看到我应该使用Platform.runLater和线程,所以我尝试:

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函数。这是我的旧版本,也不起作用:

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);

}

问题出在这段文本上,它应该改为“已连接”。只有当我的场景即将切换时,它才会发生变化......

1
只是在问,为什么你把 Platform.runLater 放在一个线程里面? - Abdullah Asendar
3个回答

4
如果您有一些长时间运行的操作,需要使用Task。否则,当该操作从JavaFX应用程序线程调用时,它将阻塞GUI。
如果您想从Task更新GUI,则必须使用Platform.runlater,它会在JavaFX应用程序线程上运行代码:Platform.runlater 对于GUI的Nodes的更新必须始终在JavaFx线程上执行。
当您在buttonListener中更新status时,它应该可以正常工作。
  button.setOnAction(evt -> {
        dbConnect();
        status.setText("Connected");
        // ...
    });

如果 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();
    });

2
你不需要在Task内部使用Platform.runLaterTask提供了一种机制来监听任务何时完成,使用setOnSucceededSetOnFailed等事件。 - leobelizquierdo

0

由于您正在连接数据库,因此应使用TaskService将此代码放在后台运行,以使GUI线程响应用户输入。只需记住,只有在GUI线程中才能更新视图状态(在您的情况下更改文本值)。您可以使用Java Thread并使用Platform.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();
}

如果你选择使用一个Task,你不需要显式地处理Platform.runLater。你只需要创建一个任务(Task类的实现),将其包装在一个Java线程中,启动它,并为不同的事件设置一个处理程序(例如: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);
    });


}

-3

好的,我通过设置task.SetonFailed()来解决了它 :)


我不明白,你是如何通过捕获一个表示任务失败的事件来解决它的问题的(如果你正在使用任务的话)? - leobelizquierdo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接