A_Elric
A_Elric

Reputation: 3568

JavaFX Text element not having changes reflected in UI

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

Answers (2)

c0der
c0der

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();
    }
}


enter image description here

Upvotes: 1

fabian
fabian

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

Related Questions