javafx文本字段不变

时间:2019-02-12 13:56:03

标签: java user-interface javafx

我有以下代码:

@FXML
private void test(){
    textField.setText("Pending...");
    boolean passed = doStuff();
    if(passed){
        textField.setText("OK");
    } else {
        textField.setText("Error");
    }
}

我想要实现的是,在doStuff()在GUI的textField中完成其工作时,应将其写为“ Pending ...”,并在完成后立即将其更改为“ OK” /“错误”。

我希望在doStuff运行时阻止GUI,以便用户必须等待并且不能单击其他内容。

但是发生的事情是,一旦我开始测试,它就会执行doStuff(),但是只会使用“ OK” /“ Error”来更新textField,但我从未看到“ Pending ...”。

我觉得我已经以某种方式更新了GUI,但是我不确定应该如何完成。

更新: 我试图在另一个线程中移动doStuff:

@FXML
private void test(){
    textField.setText("Pending...");
    Thread t = new Thread(){
        public void run(){
            boolean passed = doStuff();
            if(passed){
                textField.setText("OK");
            } else {
                textField.setText("Error");
            }
        }
    };
    t.start();
    t.join();
}

如果我要删除t.join(),它将起作用。命令,但不会阻止用户界面。所以我现在很茫然。

谢谢

1 个答案:

答案 0 :(得分:1)

您绝不能在 JavaFX Application Thread 上运行长时间运行的任务。这样做将防止所述线程执行任何与GUI相关的操作,从而导致UI冻结。这使您的用户感到难过。但是,将长期运行的任务放在后台任务上的尝试存在缺陷。您调用Thread.join,它将阻塞调用线程,直到目标线程死亡为止;实际上,这与在调用线程上运行任务是一样的。

要快速修复示例,您可以执行以下操作:

@FXML
private void test(){
    textField.setText("Pending...");
    Thread t = new Thread(){
        @Override public void run(){
            boolean passed = doStuff();
            Platform.runLater(() -> {
                if(passed){
                    textField.setText("OK");
                } else {
                    textField.setText("Error");
                }
            });
        }
    };
    t.start();
}

这将创建一个线程,将其启动,并使其在后台运行,同时让 JavaFX Application Thread 继续执行所需的操作。在后台线程内,您必须在TextField调用内更新Platform.runLater(Runnable)。这是必需的,因为您必须永远不要从JavaFX Application Thread之外的其他线程更新实时场景图;这样做将导致不确定的行为。另外,您应该查看“implements Runnable” vs “extends Thread” in Java。这样做比较好,或者至少是惯用的:

Thread t = new Thread(() -> { /* background code */ });

您还可以使用javafx.concurrent.Task,这可以使与 JavaFX Application Thread 的通信更加容易。一种选择是:

@FXML
private void test(){
    textField.setText("Pending...");
    Task<Boolean> task = new Task<>() {
        @Override protected Boolean call() throws Exception {
            return doStuff();
        }
    };
    task.setOnSucceeded(event -> textField.setText(task.getValue() ? "Ok" : "Error"));
    new Thread(task).start();
}

您还可以将TextField绑定到message的{​​{1}}属性,并在Task方法内调用updateMessage("Pending...")。如果可能的话,您甚至可以提供更详细的消息。

也就是说,自己创建并启动call并不理想,您应该研究thread pooling(使用类似Thread的方法)。您可能还想研究javafx.concurrent.Service来“重用” ExecutorService


有关JavaFX并发的更多信息,请参见Concurrency in JavaFX并阅读javafx.concurrent中类的文档。有关Java多线程的基础知识,请参见 The Java™Tutorials 中的Lesson: Concurrency