我正在设计一个匹配的记忆游戏,我几乎完成了所有工作,一切都按预期进行,但是,当用户打开两张不同的卡时,程序不会暂停(等待)几秒钟,所以用户可以看到第二张卡是什么。 我尝试使用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();
}
}
}
}
答案 0 :(得分:1)
您的代码中的所有内容都在JavaFX Application Thread中运行。您不想暂停此线程,因为它将锁定您的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();
}
答案 1 :(得分:0)
FX附带了丰富的动画/时间轴支持-无需退回到裸线程。获取wait-for-xx的最简单形式是使用配置有xx的时间轴和一个在准备好时会执行某些操作的actionHandler:
Timeline holdTimer = new Timeline(new KeyFrame(
Duration.seconds(2), e -> closeCards()));
同样,最好将控件的所有逻辑集中在外部。实际上,绝对不应出于包含视图无关逻辑的目的而扩展视图。因此,您的长期目标应该是
因为我不想破坏这样做的乐趣-我将在最后一个项目符号的方向上张贴一些轮廓。迄今为止的职责
这些片段只是稍微重新混合了您的代码,将游戏逻辑从按钮移到了控制器中(又名:这里只是外部类),并设置了按钮的动作处理程序来访问控制器。
private Card firstCard;
private Card secondCard;
private Timeline holdTimer = new Timeline(new KeyFrame(
Duration.millis(2000), e -> closeCards()));
public void closeCards() {
if (firstCard == null || secondCard == null) {
System.out.println("error!!");
return;
}
if (firstCard.isEqual(secondCard)) {
System.out.println("success");
firstCard.setDisable(true);
secondCard.setDisable(true);
firstCard = null;
secondCard = null;
} else {
firstCard.close();
secondCard.close();
firstCard = null;
secondCard = null;
}
}
public void openCard(Card card) {
if (card.isCardOpen()) return;
if (holdTimer.getStatus() == Status.RUNNING) return;
if (firstCard == null) {
firstCard = card;
firstCard.open();
} else if (secondCard == null) {
secondCard = card;
secondCard.open();
holdTimer.playFromStart();
}
}
// Dont! dont, dont!!! ever extend a Control
//**TBD**: Move open/close state logic into a CardModel
// then configure a plain Button with the properies of that model
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;
setPrefSize(150, 150);
setOnAction(e -> openCard(this));
}
public void close() {
setText("");
}
public void open() {
setText(imageLocation);
System.out.println("Open");
}
public boolean isCardOpen() {
return getText() != null && getText().length() > 0;//this.getGraphic()!=null;
}
private boolean isEqual(Card selectedCard) {
if (selectedCard == null) return false;
return this.imageLocation.equals(selectedCard.imageLocation);
}
}