Javafx 实时线程更新
Javafx live thread updates
我正在同时使用 Javafx 和线程,我经常 运行 解决这个问题,我创建了一个按钮,然后在单击该按钮时(使用事件处理程序)我创建了一个 for 循环来更改按钮到 1、2、3、4、5,然后在每个中间延迟一秒钟。就像倒计时!
但是发生的事情是延迟 5 秒并将按钮的文本更改为 5。
问题是我想看到它在 1 和 5 之间变化,但我看到的只是 5 秒延迟结束时的 5。我假设它改变了按钮文本,但我没有看到它。我可能需要处理 Javafx
class 中的 .show()
方法。
public class HewoWorld extends Application implements EventHandler<ActionEvent>
{
Thread t = new Thread();
Button butt;
boolean buttWasClicked = false;
Circle circ1 = new Circle(40, 40, 30, Color.RED);
Circle circ2 = new Circle(100, 100, 30, Color.BLUE);
Group root;
Scene scene;
Stage disStage = new Stage();
int i = 1;
public static void main(String[] args)
{
launch(args);
}
public void start(Stage stage) throws Exception
{
disStage.setTitle("tests stuffs");
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
double windh = bounds.getHeight()/2+150;//sets height of screen
double windw = bounds.getWidth()/3;//sets width of screen
Pane layout = new Pane();
butt = new Button();
butt.setText("Hello world");
root = new Group(circ1, circ2, butt);
scene = new Scene(root, 800, 400);
disStage.setWidth(windw);
disStage.setHeight(windh);
butt.setLayoutX(200);
butt.setLayoutY(200);
butt.setOnAction(this);
disStage.setScene(scene);
disStage.show();
}
public void handle(ActionEvent event)
{
if (event.getSource() == butt && buttWasClicked == false)
{
try
{
butt.setText(i+"");
t.sleep(1000);
i++;
}
catch(Exception q)
{
}
circ1 = new Circle(40, 40, 30, Color.BLACK);
circ2 = new Circle(100, 100, 30, Color.RED);
}
}
}
你要做的就是用下面的方法替换线程使用:
scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(
new Runnable(){
@Override
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
//Here your code to change the number by for example incrementig the value of the button
}
});
}
},
1000,
80,
TimeUnit.MILLISECONDS);
如果有帮助+1 :D
为什么你的代码不起作用
您的代码不起作用的原因是您阻塞了 FX 应用程序线程。
与(几乎?)所有 UI 工具包一样,JavaFX 是一个单线程 UI 工具包。这意味着所有事件处理程序和 UI 的所有呈现都在单个线程(称为 FX 应用程序线程)上执行。
在您的代码中,您有一个事件处理程序需要超过一秒才能到达 运行,因为它通过调用 Thread.sleep(...)
暂停了一秒钟。虽然该事件处理程序是 运行ning,但无法重绘 UI(因为单个线程不能同时做两件事)。因此,虽然按钮文本的值已立即更改,但新值实际上不会呈现在屏幕上,直到 handle(...)
方法完成 运行ning。如果 handle 方法中有一个 for
循环,则在整个循环(以及方法中的任何其他内容)完成之前不会呈现任何内容。
如何修复
在 JavaFX 中执行所需操作的最简单方法是使用 Timeline
来处理暂停。 Timeline
为您适当地管理线程:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountingButton extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Count");
Timeline timeline = new Timeline();
for (int count = 0; count <= 5 ; count++) {
final String text = Integer.toString(count);
KeyFrame frame = new KeyFrame(Duration.seconds(count), event ->
button.setText(text));
timeline.getKeyFrames().add(frame);
}
button.setOnAction(e -> timeline.play());
primaryStage.setScene(new Scene(new StackPane(button), 120, 75));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
一般来说,为了在特定时间点改变用户界面的外观,JavaFX Animation API (see also the tutorial) can be useful, especially Timeline
and PauseTransition
。
一种 "lower-level" 的方法是自己创建一个 Thread
并在该线程中暂停。这要高级得多:您需要小心更新 FX 应用程序线程上的 UI,而不是您创建的线程。您可以通过调用 Platform.runLater(...)
:
来完成此操作
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CountingButton extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Start");
button.setOnAction(e -> {
Thread thread = new Thread(() -> {
for (int i = 0; i <= 5 ; i++) {
final String text = "Count: "+i ;
Platform.runLater(() -> button.setText(text));
try {
Thread.sleep(1000);
} catch (InterruptedException exc) {
exc.printStackTrace();
}
}
});
thread.start();
});
primaryStage.setScene(new Scene(new StackPane(button), 120, 75));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
有关 JavaFX 中线程的更多一般信息,请查看此 post:
在这种情况下,您需要一个每秒 运行 的计时器,并在每次命中时增加一个计数器。据我所知,在 javafx 中制作计时器的最佳方法是使用时间轴。 .
int i = 0;// class field
// ....
Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), event ->
button.setText(++i)));
fiveSecondsWonder.setCycleCount(5);// repeat five times
fiveSecondsWonder.play();
我正在同时使用 Javafx 和线程,我经常 运行 解决这个问题,我创建了一个按钮,然后在单击该按钮时(使用事件处理程序)我创建了一个 for 循环来更改按钮到 1、2、3、4、5,然后在每个中间延迟一秒钟。就像倒计时!
但是发生的事情是延迟 5 秒并将按钮的文本更改为 5。
问题是我想看到它在 1 和 5 之间变化,但我看到的只是 5 秒延迟结束时的 5。我假设它改变了按钮文本,但我没有看到它。我可能需要处理 Javafx
class 中的 .show()
方法。
public class HewoWorld extends Application implements EventHandler<ActionEvent>
{
Thread t = new Thread();
Button butt;
boolean buttWasClicked = false;
Circle circ1 = new Circle(40, 40, 30, Color.RED);
Circle circ2 = new Circle(100, 100, 30, Color.BLUE);
Group root;
Scene scene;
Stage disStage = new Stage();
int i = 1;
public static void main(String[] args)
{
launch(args);
}
public void start(Stage stage) throws Exception
{
disStage.setTitle("tests stuffs");
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
double windh = bounds.getHeight()/2+150;//sets height of screen
double windw = bounds.getWidth()/3;//sets width of screen
Pane layout = new Pane();
butt = new Button();
butt.setText("Hello world");
root = new Group(circ1, circ2, butt);
scene = new Scene(root, 800, 400);
disStage.setWidth(windw);
disStage.setHeight(windh);
butt.setLayoutX(200);
butt.setLayoutY(200);
butt.setOnAction(this);
disStage.setScene(scene);
disStage.show();
}
public void handle(ActionEvent event)
{
if (event.getSource() == butt && buttWasClicked == false)
{
try
{
butt.setText(i+"");
t.sleep(1000);
i++;
}
catch(Exception q)
{
}
circ1 = new Circle(40, 40, 30, Color.BLACK);
circ2 = new Circle(100, 100, 30, Color.RED);
}
}
}
你要做的就是用下面的方法替换线程使用:
scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(
new Runnable(){
@Override
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
//Here your code to change the number by for example incrementig the value of the button
}
});
}
},
1000,
80,
TimeUnit.MILLISECONDS);
如果有帮助+1 :D
为什么你的代码不起作用
您的代码不起作用的原因是您阻塞了 FX 应用程序线程。
与(几乎?)所有 UI 工具包一样,JavaFX 是一个单线程 UI 工具包。这意味着所有事件处理程序和 UI 的所有呈现都在单个线程(称为 FX 应用程序线程)上执行。
在您的代码中,您有一个事件处理程序需要超过一秒才能到达 运行,因为它通过调用 Thread.sleep(...)
暂停了一秒钟。虽然该事件处理程序是 运行ning,但无法重绘 UI(因为单个线程不能同时做两件事)。因此,虽然按钮文本的值已立即更改,但新值实际上不会呈现在屏幕上,直到 handle(...)
方法完成 运行ning。如果 handle 方法中有一个 for
循环,则在整个循环(以及方法中的任何其他内容)完成之前不会呈现任何内容。
如何修复
在 JavaFX 中执行所需操作的最简单方法是使用 Timeline
来处理暂停。 Timeline
为您适当地管理线程:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountingButton extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Count");
Timeline timeline = new Timeline();
for (int count = 0; count <= 5 ; count++) {
final String text = Integer.toString(count);
KeyFrame frame = new KeyFrame(Duration.seconds(count), event ->
button.setText(text));
timeline.getKeyFrames().add(frame);
}
button.setOnAction(e -> timeline.play());
primaryStage.setScene(new Scene(new StackPane(button), 120, 75));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
一般来说,为了在特定时间点改变用户界面的外观,JavaFX Animation API (see also the tutorial) can be useful, especially Timeline
and PauseTransition
。
一种 "lower-level" 的方法是自己创建一个 Thread
并在该线程中暂停。这要高级得多:您需要小心更新 FX 应用程序线程上的 UI,而不是您创建的线程。您可以通过调用 Platform.runLater(...)
:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CountingButton extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Start");
button.setOnAction(e -> {
Thread thread = new Thread(() -> {
for (int i = 0; i <= 5 ; i++) {
final String text = "Count: "+i ;
Platform.runLater(() -> button.setText(text));
try {
Thread.sleep(1000);
} catch (InterruptedException exc) {
exc.printStackTrace();
}
}
});
thread.start();
});
primaryStage.setScene(new Scene(new StackPane(button), 120, 75));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
有关 JavaFX 中线程的更多一般信息,请查看此 post:
在这种情况下,您需要一个每秒 运行 的计时器,并在每次命中时增加一个计数器。据我所知,在 javafx 中制作计时器的最佳方法是使用时间轴。 .
int i = 0;// class field
// ....
Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), event ->
button.setText(++i)));
fiveSecondsWonder.setCycleCount(5);// repeat five times
fiveSecondsWonder.play();