首页 > 解决方案 > 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);

    }
});

标签: multithreadingjavafx

解决方案


正如评论中所指出的,您实际上根本没有在这里创建任何线程。您所做的只是安排三个代码块在未来某个时间点一个接一个地在 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();

});

推荐阅读