首页 > 解决方案 > 使用多线程暂停我的应用程序未按预期运行

问题描述

我正在设计一个匹配的记忆游戏,我几乎完成了它并且一切正常,但是,当用户打开两张不同的卡片时,程序不会暂停(等待)几秒钟,所以用户可以看到什么第二张牌是。我尝试过使用 long for 循环操作,但遇到了同样的问题。我试过 Thread.sleep、TimeUnit.SECONDS.sleep、Task 和 Platform.runLater。

该程序打开卡并立即关闭它然后它等待指定的持续时间,请记住我在打开和关闭函数之前调用 pauseThread。

我已经尝试了上述建议,但它们让我无处可去,我似乎无法找到我的代码的问题所在,或者我应该在哪里放置 pauseThread。提前致谢。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class MemoryMatchingGame extends Application{
    private static Card selectedCard=null;                  // This is to save a reference for the first card to use in comparison
    private static int numOfCorrectPairs = 0;       // Keeping track of how many cards the user got correct

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        String[] images = {"C:\\Users\\userName\\Desktop\\Project#4\\1.png",            // This is a string array to store images locations
                "C:\\Users\\userName\\Desktop\\Project#4\\2.png",
                "C:\\Users\\userName\\Desktop\\Project#4\\3.jpg",
                "C:\\Users\\userName\\Desktop\\Project#4\\4.jpg",
                "C:\\Users\\userName\\Desktop\\Project#4\\5.jpg",
                "C:\\Users\\userName\\Desktop\\Project#4\\6.png",
                "C:\\Users\\userName\\Desktop\\Project#4\\7.jpg",
                "C:\\Users\\userName\\Desktop\\Project#4\\8.jpg"};
        ArrayList<Card> listOfCards = new ArrayList<Card>();

        for(int i=0; i<images.length; i++) {                                            // This for loop will add each image twice to the array list
            listOfCards.add(new Card(images[i]));
            listOfCards.add(new Card(images[i]));
        }
        Collections.shuffle(listOfCards);                                               // Shuffling the deck of cards


        primaryStage.setTitle("Memory Matching Game");
        HBox hb = new HBox();

        VBox firstColoumn = new VBox();
            for(int i=0; i<4; i++) 
                firstColoumn.getChildren().add(listOfCards.get(i));
        VBox secondColoumn = new VBox();
            for(int i=4; i<8; i++) 
                secondColoumn.getChildren().add(listOfCards.get(i));
        VBox thirdColoumn = new VBox();
            for(int i=8; i<12; i++) 
                thirdColoumn.getChildren().add(listOfCards.get(i));
        VBox fourthColoumn = new VBox();
            for(int i=12; i<16; i++) 
                fourthColoumn.getChildren().add(listOfCards.get(i));

        hb.getChildren().addAll(firstColoumn, secondColoumn, thirdColoumn, fourthColoumn);

        Scene scene = new Scene(hb, 460, 450);
        primaryStage.setScene(scene);

        primaryStage.show();
    }

    private class Card extends Button {
        private String imageLocation;       // To store the destination of the image
        private Image img;                  // To store a reference of the image to be used when setting graphic on a button

        public Card(String imageLocation) throws FileNotFoundException {
            this.imageLocation = imageLocation;
            FileInputStream fis = new FileInputStream(imageLocation);
            img = new Image(fis);

            setPrefSize(150, 150);

            setOnMouseClicked(e -> {
                if(isCardOpen()==true)
                    return;             // To ensure no action is made once an image is already opened and the user clicked on it again

                if(selectedCard==null) {// This will test if the user has a card open already for comparison or not, if not it will store a reference to the card to use to compare once another card is opened
                    selectedCard = this;
                    open();
                }
                else {                  // If we enter this statement, this means the user has a card open already and we are ready to perform comparison

                    open();             // First action taken is to reveal the second card then perform comparison

                        if(this.isEqual(selectedCard)) {
                            numOfCorrectPairs++;
                            System.out.println("Got one");
                        }
                        else {
                            //Get program to pause here

                            Hold pauseThread = new Hold();
                            pauseThread.run();

                            System.out.println("After pausing");
                            this.close();
                            selectedCard.close();
                        }

                        selectedCard=null;      // This will nullify the variable so that we are able to perform comparison again for two other cards
                }                       // End of else statement

            });                         // End of actionHandler

            close();                    // This will ensure whenever a card is created it is set face-down
        }


        private void close() {
            setGraphic(null);
        }
        public void open() {
            setGraphic(new ImageView(img));
            System.out.println("Open");
        }

        private boolean isCardOpen() {
            return this.getGraphic()!=null;
        }

        private boolean isEqual(Card selectedCard) {
            return this.imageLocation.equals(selectedCard.imageLocation);
        }
    }

    private class Hold extends Thread{
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

标签: javajavafx

解决方案


您代码中的所有内容都在 JavaFX 应用程序线程中运行。你不想暂停这个线程,因为它会锁定你的 GUI。正如已经提到的,您正在启动另一个线程并将其置于睡眠状态,但这不会增加在 JavaFX 线程中运行的 GUI 的延迟。

另一种方法是使用 Platform.runLater()。Hold 线程可以调用 JavaFX 线程中实现 Platform.runLater() 可运行的方法。runnable 是一个简短的 lambda,其中包含关闭所选卡片的代码。时间可能与 3000 毫秒略有不同,但您在 JavaFX 线程中并没有发生太多事情,而且对于这个应用程序来说似乎并不重要。

以下是要尝试的修改。

首先修改 Hold 类以包含一个构造函数以传入 Card 对象。然后在卡片上调用 closeAfterPause() 方法。

private class Hold extends Thread {
    private Card card;
    public Hold(Card card) {
        this.card = card;
    }
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(3);
            card.closeAfterPause();

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

然后在 MemoryMatchingGame 类中创建 closeAfterPause() 方法。

 private void closeAfterPause() {
        Platform.runLater(() -> {
            System.out.println("After Pausing");
            close();
            selectedCard.close();
        });
    }

然后修改 if-else 语句的 else 部分,如下所示

          else {
                    //Get program to pause here
                    Hold pauseThread = new Hold(this);
                    new Thread(pauseThread).start();
                }

推荐阅读