JavaFX:Button永远不会设置图形

时间:2015-06-18 17:46:20

标签: java javafx controller

编辑:

我的代码的MCVE版本已经用于帮助调试它。它重现了我的错误。我的代码的目的是做一个记忆游戏。这意味着当轮到你时,你打开"一张卡片,然后是另一张卡片。如果他们形成一对,他们就不会被翻身,他们会保持开放。否则,你将它们翻过来并尝试在下一回合找到一对。

简单地说,错误是:当你打开转牌的第二张牌并且两张牌都没有形成一对时,第二张牌永远不会被打开!

希望这个版本的代码可以帮助您找到错误,这对我有很大的帮助!

我已将代码放在Github上:https://gist.github.com/anonymous/e866671d80384ae53b53 (你会在问题的最后找到附加

问题说明

我很高兴在JavaFX中做一个小内存游戏,我遇到了这种奇怪的行为,我点击的卡片(由扩展Button类的自定义类表示)从不改变显示的图像。

通常情况下,当我点击卡片时,它会打开"本身通过更改它显示的图形。 奇怪而恼人的是它只发生在特定的情况下。

当我打开&#34>我的卡的行为是正确的。玩家回合的第一张牌。它也适用于我打开"第二个,两张卡都是一对。可悲的是,它只适用于我想打开第二张卡并且与第一张卡不匹配的情况。

我通过添加openCard()closeCard()方法修改了Button类。这些方法将在按钮卡上设置一个特定的图形。

我现在将展示一些代码,但很难说出可能导致此行为发生的部分。更重要的是,我正在使用Eclipse,但无法弄清楚如何使用断点调试JavaFX应用程序(我正在使用控制台打印),因为当我到达断点并开始爬行时,应用程序最终会崩溃代码。

代码

首先,修改后的Button类:

public class Card extends Button{
private String cardDesign;

public Card(int row, int column){
  this.setGraphic(new ImageView("/resources/card_back.png"));
  this.setBackground(new Background(new BackgroundFill(Color.SLATEGRAY,
      new CornerRadii(6), null)));
}

public void setOpenCardDesign(String design){ cardDesign = design; }

public void openCard(){ this.setGraphic(new ImageView(cardDesign)); }

public void closeCard(){
  this.setGraphic(new ImageView("/resources/card_back.png"));
}

}

现在是控制器类,事件在MouseEvent上设置。这个控制器中有更多的代码(比如检查是否有一对),但这不是一个问题,我认为问题已经出现在我称之为打开卡的方法的行上。 我在这里使用getSource()方法因为我的卡被安排在gridPane中,我需要知道哪一个被点击了。

@Override
public void handle(MouseEvent event) {
  //Get the card that was clicked on
  Card card = (Card) event.getSource();
  //Open the card
  card.openCard();
  //Do some more after this...

}

根据我的想法,这几乎就是这样。

如前所述,我试图检查是否正在调用方法openCard()。就像在我的控制台中打印出的一些评论一样。我甚至在我设置图形的行之前和之后添加了一些控制台打印,它们都出现了。我无法确切知道当我的应用到达setGraphic()行时会发生什么,因为我的应用中没有任何内容显示(卡片仍处于关闭状态)。

任何提示都会有所帮助,因为我现在正在疯狂地沉沦。 提前谢谢。

我的代码的MCVE版本

卡片对象Card.java

package memory;

import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;

public class Card extends Button{
//-------------------------------------------------
  //Store the position of the card
  private int row;
  private int column;
//-------------------------------------------------
  //Constructor
  public Card(int row, int column){
    //Give the cards a specific color at init
    this.setBackground(new Background(new BackgroundFill(Color.DEEPSKYBLUE,
        new CornerRadii(6), null)));
    this.setText("CLOSED");
    this.row = row;
    this.column = column;
  }
//-------------------------------------------------
  //Open the card
  public void openCard(){     
    System.out.println("OPEN");
    //Cards are red when open
    this.setBackground(new Background(new BackgroundFill(Color.RED,
      new CornerRadii(6), null)));
    this.setText("OPEN");
  }
//-------------------------------------------------
  //Close the card
  public void closeCard(){
    System.out.println("CLOSE");
    //Cards are blue when closed
    this.setBackground(new Background(new BackgroundFill(Color.DEEPSKYBLUE,
        new CornerRadii(6), null)));
    this.setText("CLOSED");
  }
//-------------------------------------------------
  //Getters for row and column info
  public int getRow() { return row; }
  public int getColumn() { return column; }
}

主要(包括应用的观点和起点)Main.java

package memory;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Main extends Application {

//-------------------------------------------------  
  //The layout and the cards
  GridPane gridCard = new GridPane();
  static Card [][] cardArray;
  //The event handler
  private static EventHandler<MouseEvent> handler;
  //The array which remembers the pairs and the reminder of last open card
  static int[][] indexArray;
  static int index;
  //Boolean array to check if the card is already open
  static boolean[][] isOpen;
  //Number of pairs to find
  static int pairs = 5;
//-------------------------------------------------
  //Cheap main
  public static void main(String[] args) {
    Application.launch(args);
  }
//-------------------------------------------------
  @Override
  public void start(Stage primaryStage) throws Exception {

    //Init the event handler
    handler = new Controller();

    //Some formatting for the grid pane
    gridCard.setHgap(10);
    gridCard.setVgap(10);
    gridCard.setPadding(new Insets(0, 10, 0, 10));
    gridCard.setAlignment(Pos.CENTER);

    //Creating our card board, index array and bool array
    cardArray = new Card [2][5];
    indexArray = new int [2][5];
    isOpen = new boolean [2][5];
    //Adding the cards to our card array
    for(int i = 0; i < 2; i++){
      for(int j = 0; j < 5; j++){
        cardArray[i][j] = new Card(i, j);
        //Make those buttons look like cards
        cardArray[i][j].setPrefHeight(100);
        cardArray[i][j].setPrefWidth(70);
        //Register the event
        cardArray[i][j].addEventHandler(MouseEvent.MOUSE_CLICKED, gameController());
        //Add those cards
        gridCard.add(cardArray[i][j], j, i);
        //Set the pairs (no randomness here)
        indexArray[i][j] = j+1;
      }
    }
    //Print out the indexes of all the cards
    System.out.println("----------------");
    System.out.println("Card indexes :");
    for (int i = 0; i < indexArray.length; i++) {
      System.out.println();
      for (int j = 0; j < indexArray[0].length; j++) {
        System.out.print(indexArray[i][j]+ " | ");
      }
      System.out.println();
    }
    System.out.println("----------------");

    //Set BorderPane
    BorderPane root = new BorderPane();
    root.setBackground(new Background(new BackgroundFill(Color.BLACK,
        CornerRadii.EMPTY, null)));  
    root.setCenter(gridCard);   

    //Set the stage
    primaryStage.setScene(new Scene(root));
    primaryStage.setTitle("Memory Test");
    primaryStage.show();



  }
//-------------------------------------------------  
  //Getter for the event handler
  public static EventHandler<MouseEvent> gameController() {
    return handler;
  }
//-------------------------------------------------
  //Getter, Setter and "resetter" for the index
  public static void resetIndex() { index = 0; }
  public static int getIndex() { return index; }
  public static void setIndex(int i) {
    index = i;
  }
//-------------------------------------------------
}

控制器Controller.java

package memory;

import javafx.event.EventHandler;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.MouseEvent;

public class Controller implements EventHandler<MouseEvent>{

//-------------------------------------------------
  @Override
  public void handle(MouseEvent event) {
    //Get the card which cas clicked on
    Card card = (Card) event.getSource();

    //If the card was already open, don't do anything
    if (!Main.isOpen[card.getRow()][card.getColumn()]) {
      //Open the card
      card.openCard();

      //We opened the first card of the turn
      if (Main.getIndex() == 0) {

        //Set the card as open
        Main.isOpen[card.getRow()][card.getColumn()] = true;
        //Remember the index
        Main.setIndex(Main.indexArray[card.getRow()][card.getColumn()]);
        System.out.println("index: "+Main.getIndex());

      //We opened the second card
      }else if (Main.getIndex() != 0) {

        //Check if it is a pair
        if (Main.getIndex() == Main.indexArray[card.getRow()][card.getColumn()]) {

          //Decrement the number of pairs
          Main.pairs--;
          //Open the second card
          Main.isOpen[card.getRow()][card.getColumn()] = true;
          //Reset the index
          Main.resetIndex();

        }else{ //Close both cards if it isn't a pair
          //Wait 0.7 second to let the player remember the cards
          try {
            Thread.sleep(700);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          //Close the current card
          card.closeCard();
          System.out.println("index : " + Main.indexArray[card.getRow()][card.getColumn()]);
          Main.isOpen[card.getRow()][card.getColumn()] = false;
          //Close the first opened card by looking at the index
          //It closes both cards with the same index, but it doesn't matter
          //as the pair hasn't been found anyway
          for (int i = 0; i < Main.indexArray.length; i++) {
            for (int j = 0; j < Main.indexArray[0].length; j++) {
              if (Main.getIndex() == Main.indexArray[i][j]) {
                Main.cardArray[i][j].closeCard();
                System.out.println("index: " + Main.indexArray[i][j]);
                Main.isOpen[i][j] = false;
              }
            }
          }
          //Reset the index of last opened card
          Main.resetIndex();
        }
      }
    }

    //Check endgame
    if (Main.pairs == 0) {
      //Show a dialog box
      Alert incorrectPairs = new Alert(AlertType.INFORMATION);
      incorrectPairs.setTitle("GAME OVER");
      incorrectPairs.setHeaderText("The game is over");
      incorrectPairs.setContentText("You found all the pairs, congrats!");
      incorrectPairs.showAndWait();
    }
  }
//-------------------------------------------------
}

1 个答案:

答案 0 :(得分:1)

您正在使用Thread.sleep(...)阻止UI线程。这可以防止任何待处理的更改被重新绘制,因此您根本看不到第一个更新;暂停完成后,您只能看到后续更新。

在UI线程上实现暂停的最简单方法是使用JavaFX动画API中的PauseTransition。基本上,你做

PauseTransition pause = new PauseTransition(Duration.millis(700));
pause.setOnFinished(e -> {
    // code to execute after pause...
});
pause.play();

在您的情况下,您可能想要禁用用户界面,因此用户在暂停期间无法点击任何内容,因此您可以执行类似

的操作
PauseTransition pause = new PauseTransition(Duration.millis(700));
pause.setOnFinished(e -> {
    card.closeCard();

    // ... etc...

    card.getParent().setDisable(false);
});

card.getParent().setDisable(true);
pause.play();