JavaFX:按钮从不设置图形
JavaFX : Button never sets a graphic
编辑:
我的代码已经制作了一个 MCVE 版本来帮助调试它。它重现了我的错误。我的代码的目的是做一个记忆游戏。这意味着当轮到你时,你 "open" 一张牌,然后是另一张。如果它们形成一对,它们就不会被翻转,它们会保持打开状态。否则,你将它们翻过来,并在下一个回合尝试找到一对。
简而言之,错误是:当你打开你轮到的第二张牌时,两张牌都没有成对,第二张永远不会打开!
希望我的这个版本的代码能帮助您找到错误,这对我有很大帮助!
我已经把代码放在 Github : https://gist.github.com/anonymous/e866671d80384ae53b53
(而且你会发现附在问题的最后)
问题说明
我很喜欢用 JavaFX 做一个小记忆游戏,我在点击卡片时遇到了这种奇怪的行为(由扩展按钮 class 的自定义 class 表示)从不更改显示的图像。
通常,当我点击卡片时,它 "opens" 本身会改变它显示的图形。
奇怪又烦人的是,它只发生在特定的情况下。
当我 "open" 玩家回合的第一张牌时,我的牌的行为是正确的。当我 "open" 第二张并且两张牌都是一对时,它也有效。可悲的是,它只在我想打开第二张卡并且它与第一张卡不匹配的情况下才有效。
我通过添加 openCard()
和 closeCard()
方法修改了按钮 class。这些方法将在按钮卡上设置特定图形。
我现在将展示一些代码,但很难说出是什么部分导致了这种行为发生。更重要的是,我正在使用 Eclipse,但无法弄清楚如何使用断点调试 JavaFX 应用程序(我正在使用控制台打印),因为当我到达断点并开始遍历代码行时,该应用程序最终会崩溃。
密码
首先是修改后的Button class :
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"));
}
}
现在控制器class,事件设置在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();
}
}
//-------------------------------------------------
}
您正在使用 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();
编辑:
我的代码已经制作了一个 MCVE 版本来帮助调试它。它重现了我的错误。我的代码的目的是做一个记忆游戏。这意味着当轮到你时,你 "open" 一张牌,然后是另一张。如果它们形成一对,它们就不会被翻转,它们会保持打开状态。否则,你将它们翻过来,并在下一个回合尝试找到一对。
简而言之,错误是:当你打开你轮到的第二张牌时,两张牌都没有成对,第二张永远不会打开!
希望我的这个版本的代码能帮助您找到错误,这对我有很大帮助!
我已经把代码放在 Github : https://gist.github.com/anonymous/e866671d80384ae53b53 (而且你会发现附在问题的最后)
问题说明
我很喜欢用 JavaFX 做一个小记忆游戏,我在点击卡片时遇到了这种奇怪的行为(由扩展按钮 class 的自定义 class 表示)从不更改显示的图像。
通常,当我点击卡片时,它 "opens" 本身会改变它显示的图形。 奇怪又烦人的是,它只发生在特定的情况下。
当我 "open" 玩家回合的第一张牌时,我的牌的行为是正确的。当我 "open" 第二张并且两张牌都是一对时,它也有效。可悲的是,它只在我想打开第二张卡并且它与第一张卡不匹配的情况下才有效。
我通过添加 openCard()
和 closeCard()
方法修改了按钮 class。这些方法将在按钮卡上设置特定图形。
我现在将展示一些代码,但很难说出是什么部分导致了这种行为发生。更重要的是,我正在使用 Eclipse,但无法弄清楚如何使用断点调试 JavaFX 应用程序(我正在使用控制台打印),因为当我到达断点并开始遍历代码行时,该应用程序最终会崩溃。
密码
首先是修改后的Button class :
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"));
}
}
现在控制器class,事件设置在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();
}
}
//-------------------------------------------------
}
您正在使用 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();