Reputation: 3568
Problem: I am trying to click on a tile, when the tile is clicked, it should display its value (Text) until a second value is clicked. When the second tile is clicked it should display it's value and then remove both the first and second tiles unless their values match. At present the first value is shown; however, the second value is never displayed in the Pane. Edit: It feels like the EventDispatchThread might be getting the best of me, but I can't think of a lightweight way to appease that beast.
Tile.java
public class Tile extends StackPane {
int val;
Text text = new Text();
Tile(int value) {
val = value;
text.setText(String.valueOf(value) );
text.setFont(Font.font(30));
text.setVisible(false);
setAlignment(Pos.CENTER);
getChildren().addAll(border, text);
setOnMouseClicked(event -> compgraphics.handleTiles(this));
}
public void toggleTile(){
if(text.isVisible()){
text.setVisible(false);
}
else{
text.setVisible(true);
}
}
}
handleTiles() function
public static void handleTiles(Tile t){
if (flip1 == null) {
flip1 = t;
flip1.toggleTile();
return;
}
if (flip2 == null) {
flip2 = t;
flip2.toggleTile();
if (flip1 != null && flip2 != null) {
if(!hasSameValue(flip1,flip2)) {
flip1.toggleTile();
flip2.toggleTile();
flip1 = null;
flip2 = null;
}
}
}
}
Upvotes: 1
Views: 235
Reputation: 18792
You need to add a pause as explained in fabian's answer.
You also need some small changes in the logic of handleTile
to get what you want (see comments).
The following is a one-file mre (you can copy paste the entire code into one file, FxMain.java
, and run):
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FxMain extends Application {
private static final int COLS = 5, ROWS = 5;
private Tile flip1, flip2;
private boolean busy = false;
@Override
public void start(Stage primaryStage){
primaryStage.setScene(new Scene(makeGrid()));
primaryStage.show();
}
private Pane makeGrid() {
GridPane grid = new GridPane();
grid.setHgap(5); grid.setVgap(5);
grid.setPadding(new Insets(5));
for(int rowIndex = 0; rowIndex < ROWS ; rowIndex++) {
//an array to hold buttons of one row
Node[] nodes = new Node[COLS];
for(int colIndex = 0; colIndex < COLS ; colIndex++) {
Tile tile= new Tile(String.valueOf(rowIndex + colIndex));
tile.setOnMouseClicked(e->handleTiles(tile));
nodes[colIndex]= tile;
}
grid.addRow(rowIndex, nodes);
}
return grid;
}
public void handleTiles(Tile t){
if(busy) return; //ignore new clicks until previous ones were processed
busy = true;
if (flip1 == null) {
flip1 = t;
flip1.toggleTile();
busy = false;
return;
}else {
flip2 = t;
flip2.toggleTile();
//set delay to 0 if values match
double duration = flip1.getValue().equals(flip2.getValue()) ? 0 : 2 ;
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(duration));
pauseTransition.setOnFinished(e->{
if(!flip1.getValue().equals(flip2.getValue())) {
flip1.toggleTile();
flip2.toggleTile();
}
flip1 = null;
flip2 = null;
busy = false;
});
pauseTransition.play();
}
}
public static void main(final String[] args) {
launch(args);
}
}
class Tile extends StackPane {
private final Text text;
Tile(String value) {
text = new Text(value);
text.setFont(Font.font(30));
text.setVisible(false);
setPrefSize(50,50);
setAlignment(Pos.CENTER);
setStyle("-fx-border-color: black");
getChildren().add(text);
}
public void toggleTile(){
text.setVisible( ! text.isVisible());
}
public String getValue(){
return text.getText();
}
}
Upvotes: 1
Reputation: 82461
The problem is that you immediately toggle both tiles on the second click. If a method runs on the JavaFX application thread (this is the case for event handlers) only the state of the GUI when the the method returns is important. For this reason for non-matching tiles the second tile is toggled twice before any layout/rendering occurs.
Use a PauseTransition
to add a delay:
public static void handleTiles(Tile t){
if (flip1 == null) {
flip1 = t;
flip1.toggleTile();
return;
}
if (flip2 == null) {
flip2 = t;
flip2.toggleTile();
if (flip1 != null && flip2 != null) {
if(!hasSameValue(flip1, flip2)) {
// hide text with delay
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(e -> {
flip1.toggleTile();
flip2.toggleTile();
flip1 = null;
flip2 = null;
});
pause.play();
}
}
}
}
BTW: I recommend adhering to the java naming conventions. Type names are camel case starting with an uppercase letter. The type containing handleTiles
does not fit this pattern.
Upvotes: 2