英文:
Use of Thread.sleep() in JavaFx Application
问题
我尝试制作一个应用程序,其中显示一个按钮。当我点击按钮时,场景应该显示一些文本(添加一个标签)几秒钟,然后文本应该消失(从场景中删除标签)。但实际上,当我点击按钮时,什么都不发生。
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
StackPane pane = new StackPane();
Button btn = new Button();
Label lb = new Label("开始");
pane.getChildren().addAll(btn);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
try {
Thread.sleep(300);
} catch (Exception ex) {
ex.printStackTrace();
}
pane.getChildren().removeAll(lb);
});
}
public static void main(String[] args) {
launch(args);
}
}
英文:
I try to make an application that shows a button. When I click the button, the scene should show some text (add a label) for a few seconds, and then the text should disappear (removing the label from the scene). But in fact when I click the button, nothing happens.
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
StackPane pane = new StackPane();
Button btn = new Button();
Label lb = new Label("Start");
pane.getChildren().addAll(btn);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
try {
Thread.sleep(300);
} catch (Exception ex) {
ex.printStackTrace();
}
pane.getChildren().removeAll(lb);
});
}
public static void main(String[] args) {
launch(args);
}
}
答案1
得分: 4
使用 PauseTransition
:
btn.setOnAction(e -> {
pane.getChildren().addAll(lb);
PauseTransition pause = new PauseTransition(Duration.seconds(0.3));
pause.setOnFinished(e -> pane.getChildren().removeAll(lb));
pause.play();
});
你的方法无法生效的原因是,JavaFX 在渲染和处理用户事件时实际上使用了单个线程。它会以固定的间隔更新屏幕(在当前实现中为每秒 60 次),但出于同步原因,必须等待当前正在处理的任何挂起事件完成。因此,你的原始代码添加了标签,暂停了 0.3 秒,然后移除了标签。整个过程中,FX Application Thread 都被占用,因此 FX 框架在此过程中没有机会重新绘制场景。
总之,你不应该阻塞 FX 应用程序线程,即不要调用 sleep()
或执行长时间运行的操作。
针对评论中的附加问题的更新:
要禁用所有事件处理,你可以在场景的根节点上调用 setDisable(true)
。因此,在显示标签时阻止事件处理:
btn.setOnAction(e -> {
pane.getChildren().addAll(lb);
pane.setDisable(true);
PauseTransition pause = new PauseTransition(Duration.seconds(0.3));
pause.setOnFinished(e -> {
pane.getChildren().removeAll(lb));
pane.setDisable(false);
});
pause.play();
});
英文:
Use a PauseTransition
:
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
PauseTransition pause = new PauseTransition(Duration.seconds(0.3));
pause.setOnFinished(e -> pane.getChildren().removeAll(lb));
pause.play();
});
The reason your approach doesn't work, is that JavaFX effectively uses a single thread for rendering and handling user events. It will update the screen at a fixed interval (60 times per second in the current implementation), but for synchronization reasons has to wait for any pending events that are currently being handled to complete first. So you original code adds the label, pauses for 0.3 seconds, and then removes the label. The FX Application Thread is occupied for this whole process, so the FX framework never has an opportunity to redraw the scene while it is happening.
The bottom line here is that you should never block the FX application thread, by calling sleep()
or by executing long-running operations.
Update in response to additional question in comment:
To disable all event handling, you can call setDisable(true)
on the root node of the scene. So to prevent event handling while the label is shown:
btn.setOnAction(e->{
pane.getChildren().addAll(lb);
pane.setDisable(true);
PauseTransition pause = new PauseTransition(Duration.seconds(0.3));
pause.setOnFinished(e -> {
pane.getChildren().removeAll(lb));
pane.setDisable(false);
});
pause.play();
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论