multithreading - JavaFX - 简单翻转卡(延迟 2 秒更改两个图像)
问题描述
我正在用卡片图像创建一个简单的记忆游戏:用户试图找到两张具有相同图像的卡片。首先,我创建一个带有闭合卡片图像的按钮。当用户点击按钮时,程序在打开的卡片图片上改变图片的图像,等待 2 秒然后改变图片。我坚持下去。
当用户单击按钮时,我在按钮的动作侦听器中运行另外三个线程:第一个在打开的图片上更改关闭的图片,第二个等待 2 秒,第三个将图片改回。但取而代之的是,ui中的更改仅在所有线程退出时发生
行动清单代码
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Runnable r1 = ()->{
synchronized (btn) {
System.out.println("test");
Image image2 = new Image(getClass().getResourceAsStream("img/open.jpg"));
ImageView iv = new ImageView(image2);
iv.setFitWidth(100);
iv.setFitHeight(100);
Platform.runLater(()->btn.setGraphic(iv));
}
};
Runnable r2 = ()->{
synchronized (btn) {
System.out.println("test2");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test2 end");
}
};
Runnable r3=()->{
synchronized (btn) {
System.out.println("test3");
Image image2 = new Image(getClass().getResourceAsStream("img/unnamed.jpg"));
ImageView iv = new ImageView(image2);
iv.setFitWidth(100);
iv.setFitHeight(100);
btn.setGraphic(iv);
}
};
Platform.runLater(r1);
Platform.runLater(r2);
Platform.runLater(r3);
}
});
解决方案
正如评论中所指出的,您实际上根本没有在这里创建任何线程。您所做的只是安排三个代码块在未来某个时间点一个接一个地在 FX 应用程序线程(您当前所在的线程)上运行。其中一个代码块将 FX 应用程序线程暂停两秒钟,这将阻止它在此期间执行它应该执行的操作(呈现 UI 和处理用户事件)。因此,您实际上看不到 中代码的结果r1
,因为 UI 在由 创建的暂停期间未呈现r2
,然后r3
立即执行。
这里只需要执行 中的代码r1
,然后在r3
两秒后执行 中的代码。最简单的方法是使用PauseTransition
:
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Image image2 = new Image(getClass().getResourceAsStream("img/open.jpg"));
ImageView iv = new ImageView(image2);
iv.setFitWidth(100);
iv.setFitHeight(100);
btn.setGraphic(iv);
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(e -> {
Image image2 = new Image(getClass().getResourceAsStream("img/unnamed.jpg"));
ImageView iv = new ImageView(image2);
iv.setFitWidth(100);
iv.setFitHeight(100);
btn.setGraphic(iv);
});
pause.play();
}
});
创建两个 s 也没有真正的理由ImageView
;您可以只更新图像,而无需在每次按下按钮时重新加载图像(如果需要,您可以在多个图像视图中使用相同的图像)。如果您对事件处理程序使用 lambda 表达式,您的代码将简化为
// you can scope these as widely as you need: you should only load them once
Image openImage = new Image(getClass().getResourceAsStream("img/open.jpg"));
Image unnamedImage = new Image(getClass().getResourceAsStream("img/unnamed.jpg"));
// this needs to be specific to this button:
ImageView iv = new ImageView();
iv.setFitWidth(100);
iv.setFitHeight(100);
btn.setOnAction(event -> {
iv.setImage(openImage);
btn.setGraphic(iv);
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(e -> iv.setImage(unnamedImage));
pause.play();
});
推荐阅读
- php - 使用 Maatwebsite\Excel 将数组导出到 Excel
- r - 如何将经纬度坐标中的栅格投影到 UTM,以便在 tmap 中绘图?
- nginx - 将请求转发到 ALB 的 NGINX 配置
- java - 控制一个整数值 null
- angular - 在 fullcalendar.io Angular 打字稿中获取选定的月份
- javascript - 过滤内部 UseEffect 反应不起作用
- node.js - 此错误源于在没有 catch 块的情况下抛出异步函数内部,或拒绝未处理的承诺
- python - 如何让 Discord 机器人在用户加入时添加角色?
- java - 为什么 throwExceptionIfNoHandlerFound 在 spring mvc 中的 web.xml 中不起作用
- javascript - 为什么它的返回未定义?