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
JAVAFX how to update GUI elements (nodes)
提问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 Task
you 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 Nodes
of your GUI have always to be performed on the JavaFx Thread.
对Nodes
GUI 的更新总是在 JavaFx 线程上执行。
When you update status
inside the Listener
of button
it should work.
当你status
在里面更新Listener
时button
它应该可以工作。
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 Task
or a Service
to 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 Thread
and use Platform.runLater
which 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 run
of 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 Scene
when 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 Task
you dont have to deal with Platform.runLater
explicitly. 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() 来修复它:)