如何在运行5秒后更新JavaFX应用程序中的文本框?
我有一个控制器类“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。我更改文本字段内容的正确方法是什么?我觉得线程造成了问题 我在我的主要方法中尝试做的是:如何在运行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
- 启动JavaFX应用程序/GUI
- 5秒后(睡眠),将FXController.java中文本字段的文本更改为“Hello World”
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跟踪控制器?您