如何在 JavaFX 的后台工作中更改 GUI?
How can I make changes in the GUI with background work in JavaFX?
通过所有的搜索和阅读,很明显我需要调用 Platform.runLater() 来更改 GUI。看来我还需要使用 Runnable 接口。也许我还应该使用 Tasks?
但我不知道我应该如何使用它们。另外,我不确定我应该把它们放在哪个 class 中。我是 JavaFX 的超级新手。
我的试用 JavaFX 项目只有一个标签和一个文本字段。 Label 包含一个问题,TextField 用于回答。够简单了。
我运行这里的问题:
答案检查方法在单独的class中。我不知道如何访问 GUI/FXML 的组件并更改它们。其他 class 中的方法是静态的,而 GUI/FXML 中的组件是非静态的。
因为我的实际项目会有很多测验,所以我热衷于使用单独的 classes 来检查答案。
这里只有 3 个小 classes 相关:
- 包含主要方法的“启动器”class。
- FXML 文件的“ViewController”class 以及一些方法。
- “Ans”class有一个检查答案输入的方法
我应该把 Platform.runLater() 放在哪个 class 中?代码会怎样?
我将只分享“Ans”和“ViewController”classes 的代码。
Ans(后台工作应该在这个文件里,我在评论里提到了我想做但不能做的事,比如我想从那里设置标签文本,但我不能。因为我不知道该怎么做,所以我只是在那里放了一个 System.out.Println。在它旁边的评论中,我已经提到了我实际上想做。)
package com.dan.ans;
import com.dan.qn.Qn;
import com.dan.view.ViewController;
public class Ans {
public static void checkAns() {
// Checks if the ans is correct.
if (ViewController.getTextFieldInput().equalsIgnoreCase(Qn.getAns())) {
System.out.println("Correct!"); // Here I want the label to say 'Correct!' rather than it be print out in the console.
Qn.setQuestion(); // This gets the next question from the database. But again, I don't know how to make the changes show on the screen. (In the actual code I'd have a separate Label for each of these things)
} else { // Runs if it's not correct.
System.out.println("Incorrect!"); // Here I want the label to say 'Incorrect' rather than it be print out in the console.
}
}
}
ViewController
package com.dan.view;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import com.dan.ans.Ans;
import com.dan.qn.Qn;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class ViewController implements Initializable {
private static String textFieldInput; // I don't know how to access the typed info in the textField from another class. So I store it here and get it from it.
// This is the getter I use for it. (See above)
public static String getTextFieldInput() {
return textFieldInput;
}
@FXML
private Label label;
@FXML
private TextField textField;
@Override
public void initialize(URL location, ResourceBundle resources) {
Qn.setQuestion(); // This method is in the Qn class. It retrieves data from the db file and keeps them in variables.
label.setText(Qn.getQn()); // This sets the label's text using the retrieved data. So you see the first question when the program opens.
}
// Event Listener on TextField[#textField].onAction
public void enter(ActionEvent event) throws IOException {
textFieldInput = textField.getText(); // Stores the typed info in the variable to be accessed from elsewhere.
Ans.checkAns(); // Runs the checkAns to check if the typed answer is correct or not.
}
}
“Launcher”方法看起来就像任何带有 main class 的方法。所以我没有在这里分享它的代码。
有人可以告诉我如何从其他 classes(例如“Ans”)更新 GUI 中的组件吗?我很确定我应该使用 Platform.runLater() 和 Runnable。也可能是任务。我看过几个例子,但不清楚如何在这种情况下使用它。
提前致谢! :)
这里不是特别清楚问题出在哪里。自然的(对我来说,无论如何)方法只是使 checkAnswer(...)
方法成为简单 "does what it says on the box" 的方法,即将答案作为参数,检查它,然后 returns 一个给调用者的值表明它是否正确。
这样你也可以避免所有丑陋的 static
黑客攻击。
public class Ans {
public boolean checkAns(String answer) {
// not really sure what Qn is here, but you can also clean this up and
// get rid of the static methods
if (answer.equalsIgnoreCase(Qn.getAns()) {
// not sure if this really belongs here?
Qn.setQuestion(); // really takes no parameters? Sets it to what, then?
return true ;
} else {
return false ;
}
}
}
然后在你的控制器中,你可以做
public class ViewController implements Initializable {
private Ans ans ;
@FXML
private Label label;
@FXML
private TextField textField;
@Override
public void initialize(URL location, ResourceBundle resources) {
ans = new Ans();
// ...
}
// ...
public void enter(ActionEvent event) {
if (ans.checkAns(textField.getText())) {
// update UI to show answer was correct, etc
} else {
// update UI to show answer was incorrect...
}
}
// ...
}
注意这如何让您保持适当的关注点分离:Ans
class 根本不需要知道 UI 的任何信息(它不应该知道几乎没有),并且所有 UI-specific 代码都封装在它所属的控制器 class 中。
不太清楚您为什么要询问 Platform.runLater(...)
并使用 Task
,因为您发布的 none 代码似乎涉及任何后台线程(即 none 似乎需要相当多的时间才能 运行)。例如,如果 checkAns(...)
方法正在执行一些远程查找并且确实需要时间到 运行,您将在 Task
中执行它并更新任务的 UI onSucceeded
处理程序。看,例如。不过,您的问题似乎更多地是关于基本的 OO 设计以及如何定义不同对象之间的关系;我不认为你实际上是在询问线程。
通过所有的搜索和阅读,很明显我需要调用 Platform.runLater() 来更改 GUI。看来我还需要使用 Runnable 接口。也许我还应该使用 Tasks?
但我不知道我应该如何使用它们。另外,我不确定我应该把它们放在哪个 class 中。我是 JavaFX 的超级新手。
我的试用 JavaFX 项目只有一个标签和一个文本字段。 Label 包含一个问题,TextField 用于回答。够简单了。
我运行这里的问题:
答案检查方法在单独的class中。我不知道如何访问 GUI/FXML 的组件并更改它们。其他 class 中的方法是静态的,而 GUI/FXML 中的组件是非静态的。
因为我的实际项目会有很多测验,所以我热衷于使用单独的 classes 来检查答案。
这里只有 3 个小 classes 相关:
- 包含主要方法的“启动器”class。
- FXML 文件的“ViewController”class 以及一些方法。
- “Ans”class有一个检查答案输入的方法
我应该把 Platform.runLater() 放在哪个 class 中?代码会怎样?
我将只分享“Ans”和“ViewController”classes 的代码。
Ans(后台工作应该在这个文件里,我在评论里提到了我想做但不能做的事,比如我想从那里设置标签文本,但我不能。因为我不知道该怎么做,所以我只是在那里放了一个 System.out.Println。在它旁边的评论中,我已经提到了我实际上想做。)
package com.dan.ans;
import com.dan.qn.Qn;
import com.dan.view.ViewController;
public class Ans {
public static void checkAns() {
// Checks if the ans is correct.
if (ViewController.getTextFieldInput().equalsIgnoreCase(Qn.getAns())) {
System.out.println("Correct!"); // Here I want the label to say 'Correct!' rather than it be print out in the console.
Qn.setQuestion(); // This gets the next question from the database. But again, I don't know how to make the changes show on the screen. (In the actual code I'd have a separate Label for each of these things)
} else { // Runs if it's not correct.
System.out.println("Incorrect!"); // Here I want the label to say 'Incorrect' rather than it be print out in the console.
}
}
}
ViewController
package com.dan.view;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import com.dan.ans.Ans;
import com.dan.qn.Qn;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class ViewController implements Initializable {
private static String textFieldInput; // I don't know how to access the typed info in the textField from another class. So I store it here and get it from it.
// This is the getter I use for it. (See above)
public static String getTextFieldInput() {
return textFieldInput;
}
@FXML
private Label label;
@FXML
private TextField textField;
@Override
public void initialize(URL location, ResourceBundle resources) {
Qn.setQuestion(); // This method is in the Qn class. It retrieves data from the db file and keeps them in variables.
label.setText(Qn.getQn()); // This sets the label's text using the retrieved data. So you see the first question when the program opens.
}
// Event Listener on TextField[#textField].onAction
public void enter(ActionEvent event) throws IOException {
textFieldInput = textField.getText(); // Stores the typed info in the variable to be accessed from elsewhere.
Ans.checkAns(); // Runs the checkAns to check if the typed answer is correct or not.
}
}
“Launcher”方法看起来就像任何带有 main class 的方法。所以我没有在这里分享它的代码。
有人可以告诉我如何从其他 classes(例如“Ans”)更新 GUI 中的组件吗?我很确定我应该使用 Platform.runLater() 和 Runnable。也可能是任务。我看过几个例子,但不清楚如何在这种情况下使用它。
提前致谢! :)
这里不是特别清楚问题出在哪里。自然的(对我来说,无论如何)方法只是使 checkAnswer(...)
方法成为简单 "does what it says on the box" 的方法,即将答案作为参数,检查它,然后 returns 一个给调用者的值表明它是否正确。
这样你也可以避免所有丑陋的 static
黑客攻击。
public class Ans {
public boolean checkAns(String answer) {
// not really sure what Qn is here, but you can also clean this up and
// get rid of the static methods
if (answer.equalsIgnoreCase(Qn.getAns()) {
// not sure if this really belongs here?
Qn.setQuestion(); // really takes no parameters? Sets it to what, then?
return true ;
} else {
return false ;
}
}
}
然后在你的控制器中,你可以做
public class ViewController implements Initializable {
private Ans ans ;
@FXML
private Label label;
@FXML
private TextField textField;
@Override
public void initialize(URL location, ResourceBundle resources) {
ans = new Ans();
// ...
}
// ...
public void enter(ActionEvent event) {
if (ans.checkAns(textField.getText())) {
// update UI to show answer was correct, etc
} else {
// update UI to show answer was incorrect...
}
}
// ...
}
注意这如何让您保持适当的关注点分离:Ans
class 根本不需要知道 UI 的任何信息(它不应该知道几乎没有),并且所有 UI-specific 代码都封装在它所属的控制器 class 中。
不太清楚您为什么要询问 Platform.runLater(...)
并使用 Task
,因为您发布的 none 代码似乎涉及任何后台线程(即 none 似乎需要相当多的时间才能 运行)。例如,如果 checkAns(...)
方法正在执行一些远程查找并且确实需要时间到 运行,您将在 Task
中执行它并更新任务的 UI onSucceeded
处理程序。看,例如