如何在运行5秒后更新JavaFX应用程序中的文本框?

如何在运行5秒后更新JavaFX应用程序中的文本框?,javafx,runnable,future,Javafx,Runnable,Future,我有一个控制器类“FXController.java”和扩展“Application”并包含启动代码的FXApplication.java。在单独的类“TestFX.java”中,我调用启动gui的“FXApplication.java”中的start方法。我希望能够访问其控制器,以便可以更改控制器文本字段中的文本。在我的FXApplication.java中,我在“launch”方法中为FXLoader创建了一个变量,并使用“getController”方法将其设置为公共变量:public F

我有一个控制器类“FXController.java”和扩展“Application”并包含启动代码的FXApplication.java。在单独的类“TestFX.java”中,我调用启动gui的“FXApplication.java”中的start方法。我希望能够访问其控制器,以便可以更改控制器文本字段中的文本。在我的FXApplication.java中,我在“launch”方法中为FXLoader创建了一个变量,并使用“getController”方法将其设置为公共变量:public FXController the controller

在TestFX.java中,在新的runnable中启动FXApplication.java的主方法中调用“start”方法后,我尝试访问控制器以更改单个文本字段的内容,我得到一个异常,该异常表示控制器为null。我更改文本字段内容的正确方法是什么?我觉得线程造成了问题

我在我的主要方法中尝试做的是:

  • 启动JavaFX应用程序/GUI
  • 5秒后(睡眠),将FXController.java中文本字段的文本更改为“Hello World”
请注意,FXApplication.java加载/使用的fxml文件已正确指向FXController.java。我想知道是否有某种方法可以访问控制器,尽管已经为FX应用程序生成了一个新的runnable

FXApplication.java

public class FXApplication extends Application {

    public FXController theController;

    public void start() {
        Application.launch(FXApplication.class, args);
    }


    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader fxmll = new FXMLLoader(getClass().getResource("fxml_example.fxml"));
        Parent root = fxmll.load();      
        theController = fxmll.getController();        
        stage.setTitle("FXML Welcome");
        stage.setScene(new Scene(root, 300, 275));
        stage.show();

    }
}
我的TestFX.java

public class TestFX {

    public static FXApplication fxApp = new FXApplication();
    public ExecutorService execs;
    public Future<?> fut;

    TestFX(ExecutorService execs) {
        this.execs = execs;
    }

    public void start() {
        fut = execs.submit(new Runnable() {
            @Override
            public void run() {
                fxApp.start();                
            }
        });
    }

    public static void main(String[] args) {
        ExecutorService execs = Executors.newCachedThreadPool();
        TestFX testFx = new TestFX(execs);
        testFx.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
        //fxApp.theController.setTextBoxText("Hello Word");
        Platform.runLater(() -> fxApp.theController.setTextBoxText("Hello Word"));


    }
公共类TestFX{
public static FXApplication fxApp=new FXApplication();
公共服务执行官;
公共未来基金;
TestFX(执行者服务执行者){
this.execs=execs;
}
公开作废开始(){
fut=execs.submit(新的Runnable(){
@凌驾
公开募捐{
fxApp.start();
}
});
}
公共静态void main(字符串[]args){
ExecutorService execs=Executors.newCachedThreadPool();
TestFX TestFX=新的TestFX(execs);
testFx.start();
试一试{
睡眠(5000);
}捕捉(中断异常e){
e、 printStackTrace();
}        
//fxApp.theController.setTextBoxText(“Hello Word”);
Platform.runLater(()->fxApp.theController.setTextBoxText(“Hello Word”));
}

您的代码有两个问题

首先,您正在调用静态
fxmloader.load(URL)
方法,而不是在
fxmloader
实例上调用
load
。因此,
fxmloader
实例永远无法初始化其控制器。您需要

    FXMLLoader fxmll = new FXMLLoader(getClass().getResource("fxml_example.fxml"));
    Parent root = fxmll.load();        
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }        
    Platform.runLater(() -> fxApp.theController.setTextBoxText("Hello Word"));
第二个问题是,然后您将从后台线程更改文本框的文本,而不是从FX应用程序线程更改文本框的文本。(除非您在控制器类中处理此问题:您不显示该类的代码。)您需要

您也可以通过暂停转换来执行此操作:

PauseTransition pause = new PauseTransition(Duration.seconds(5));
pause.setOnFinished(event -> fxApp.theController.setTextBoxText("Hello Word"));
pause.play();

你想这样做的东西,你应该使用Task类。这为你做了所有繁重的工作,你所要做的就是设置你想在任务完成时运行FXAT的代码。下面是一个例子,我省略了控制器的东西,因为它只是混淆了概念:

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {

    private static Label label;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        label = new Label();
        label.setText("Waiting...");
        StackPane root = new StackPane();
        root.getChildren().add(label);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();

        Task<Void> sleeper = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
                return null;
            }
        };
        sleeper.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent event) {
                label.setText("Hello World");
            }
        });
        new Thread(sleeper).start();
    }
}
导入javafx.application.application;
导入javafx.concurrent.Task;
导入javafx.concurrent.WorkerStateEvent;
导入javafx.event.EventHandler;
导入javafx.scene.scene;
导入javafx.scene.control.Label;
导入javafx.scene.layout.StackPane;
导入javafx.stage.stage;
公共类HelloWorld扩展了应用程序{
专用静态标签;
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage){
setTitle(“你好,世界!”);
标签=新标签();
label.setText(“等待…”);
StackPane root=新的StackPane();
root.getChildren().add(标签);
原始阶段。设置场景(新场景(根,300250));
primaryStage.show();
任务睡眠者=新任务(){
@凌驾
受保护的Void调用()引发异常{
试一试{
睡眠(5000);
}捕捉(中断异常e){
}
返回null;
}
};
sleeper.setonSucceed(新的EventHandler(){
@凌驾
公共无效句柄(WorkerStateEvent事件){
label.setText(“Hello World”);
}
});
新螺纹(枕木)。开始();
}
}

“睡眠者”任务除了睡眠之外什么都不做,但它将在一个新线程上睡眠,以便FXAT可以继续响应屏幕活动。然后,睡眠完成后,SUCCESS的事件处理程序将在实例化任务的线程上运行,在本例中是FXAT。

控制器类中唯一的方法是方法“setTextBoxText”与@FXML绑定。从您描述的第二个问题开始,在TestFX.java的主方法被调用两次的情况下(因此同时启动了两个FXGui实例)如何获得每个控制器的引用/句柄,使其独立运行?关于控制器的问题是
setTextBoxText
是否在FX应用程序线程上安排UI更新。如果不是,则必须在该线程上安排对该方法的调用,如答案所示。我不太理解您的要求关于控制器的定义;您的
FXApplication
类公开了对控制器的引用;每个
FXApplication
实例都将有自己的控制器引用。我不这么认为,setTextBoxText(thetext)方法包含的所有内容都是“oneTextBox.setText(thetext)”。关于我的问题,如果在我的主方法中调用了testFx.start()两次。它应该为FX应用程序启动两个单独的线程,对吗?如何从TextFX.java跟踪控制器?您