简体   繁体   English

JavaFX:单击按钮后,游戏场景未更新

[英]JavaFX: game scene doesn't get updated after button click

I have a Main , Cell and Displayer classes. 我有一个MainCellDisplayer类。 Main creates a Cell[][] grid, passes it to Displayer that constructs Pane s and returns a game Scene . Main创建一个Cell[][]网格,将其传递到Displayer ,该Displayer构造Pane并返回游戏Scene

I have a deleteCells button that calls deleteCells method inside Main class. 我有一个deleteCells按钮, deleteCellsMain类中调用deleteCells方法。 However, after I press it, the Scene doesn't get updated, even though System.out.println output proves the method was executed: 但是,在按下它之后,即使System.out.println输出证明该方法已执行,该Scene也不会得到更新:

Cell : 单元格

public class Cell {
    private boolean status;

    public Cell()  {
        System.out.println("DEAD CELL CREATED");

        status = false;
    }

    public boolean getStatus()  {
            return status;
    }

    public void setStatus(boolean status)  {
        this.status = status;
    }

}

Main : 主要

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.Random;

public class Main extends Application {
    private static int  gameWidth= 800;
    private static int gameHeight=600;

    private static int gridSize = 20;
    private static Cell[][] grid = new Cell[gridSize][gridSize];

    private static Scene scene;
    private static Displayer gameDisplayer;    

    @Override
    public void start(Stage primaryStage) throws Exception{
        primaryStage.setTitle("Advanced Game of Life");

        createGrid();

        gameDisplayer = new Displayer(gameWidth, gameHeight, grid);

        scene = gameDisplayer.getGameScene();
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }

    private static void createGrid()  {
        for (int i=0; i<gridSize; i++)  {
            for (int j=0; j<gridSize; j++)
                grid[i][j] = new Cell();
        }
    }

    public static void deleteCells()  {
        for (int i=0; i<gridSize; i++)  {
            for (int j=0; j<gridSize; j++) {
                grid[i][j].setStatus(false);
            }
        }

        //scene = gameDisplayer.getGameScene();  //this doesn't work
    }

    public static void createCell()  {
        Random rand = new Random();
    }
}

Displayer : 显示器

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

public class Displayer  implements EventHandler<ActionEvent> {
    private static Color ALIVE_COLOR=Color.GREEN;
    private static Color DEAD_COLOR=Color.SILVER;;

    private static BorderPane gamePane;
    private static Pane cellsPane;

    private HBox buttonsPane;
    private Pane statsPane;
    private Pane setupsPane;
    private HBox bottomPane;

    Button createCellButton;
    Button deleteCellsButton;

    Label cellsCountLabel;
    Label setupsLabel;

    private int  gameWidth;
    private int gameHeight;

    static int gridSize = 20;

    static int cellId = 1;

    private Cell[][] gameGrid;
    private Scene gameScene;

    public Displayer(int width, int height, Cell[][] grid)  {
        gameWidth = width;
        gameHeight = height;

        gameGrid=grid;

        createPanes();
        createButtons();
        createLabels();

        setPaneStyles();
        setButtonsStyles();
        setLabelsStyles();


        gameScene = new Scene(gamePane, gameWidth, gameHeight);
    }

    public Scene getGameScene()  {
            return gameScene;
    }

    private void createPanes()  {
        gamePane = new BorderPane();

        buttonsPane = new HBox(5);
        statsPane = new Pane();
        cellsPane = makeGridPane(gameGrid);
        setupsPane = new Pane();
        bottomPane = new HBox(5);
    }

    private void createButtons()  {
        createCellButton = new Button();
        deleteCellsButton = new Button();
    }

    private void createLabels()  {
        cellsCountLabel = new Label("Cells Count: " + (cellId + 1));
        setupsLabel = new Label("Setups Label");
    }



    private void setPaneStyles()  {...}

    private void setButtonsStyles()  {...}

    private void setLabelsStyles()  {...}

    public void handle(ActionEvent event)  {
        if (event.getSource()==createCellButton)  {
           //Main.createCell();
        }
        else if (event.getSource() == deleteCellsButton) {
            Main.deleteCells();
            cellsCountLabel.setText("Cells Count: " + (cellId + 1));
            System.out.println("Cells deleted");
        }
        else  {
            System.out.println("Unknown button");
        }
    }

    public Pane makeGridPane(Cell[][] grid) {
        Pane gridPane = new Pane();

        for(int i=0; i<gridSize; i++){
            for(int j=0; j<gridSize; j++){
                Rectangle rect = new Rectangle();

                System.out.println("grid[" + i + "][" + j +"]");
                if (grid[i][j].getStatus())  {
                        rect.setFill(ALIVE_COLOR);
                }
                else {
                    rect.setFill(DEAD_COLOR);
                }

                rect.setStroke(Color.BLACK);

                rect.setX(i * gridSize);
                rect.setY(j * gridSize);
                rect.setWidth(gridSize);
                rect.setHeight(gridSize);

                rect.setOnMouseClicked(new EventHandler<MouseEvent>(){
                    @Override
                    public void handle(MouseEvent me){
                            rect.setFill(Color.RED);
                    }
                });

                gridPane.getChildren().add(rect);


            }
        }

        return gridPane;
    }
}

Is there way to make the Scene update itself, even though it is constructed inside Displayer and the buttons call methods inside Main class? 即使它是在Displayer构造的,并且Button调用了Main类中的方法,也可以使Scene更新本身吗? I tried adding scene = gameDisplayer.getGameScene(); 我尝试添加scene = gameDisplayer.getGameScene(); inside deleteCells() method, but it didn't change the situation. deleteCells()方法中,但并没有改变情况。 How should I handle user's input, in accordance with MVC, so that the Scene responded to changes, given all GUI elements are located in a separate class Displayer ? 鉴于所有GUI元素都位于单独的Displayer类中,我应该如何按照MVC处理用户的输入,以使Scene响应更改?

EDIT : 编辑

Added a editGrid() method to Main : Main添加了editGrid()方法:

public static void editGrid(int x, int y, boolean status)  {
        grid[x][y].setStatus(status);
}

Updated setOnMouseClicked inside Displayer : 更新setOnMouseClickedDisplayer

rect.setOnMouseClicked(new EventHandler<MouseEvent>(){
   @Override
   public void handle(MouseEvent me){
       Main.editGrid ((int) rect.getX()/gridSize, (int) rect.getY()/gridSize, true);
   }
});

In traditional MVC, the model is "observable", in the sense that observers can register to receive notifications of changes to the data. 在传统的MVC中,在观察者可以注册以接收数据更改通知的意义上,该模型是“可观察的”。 The view (or controller, depending on the variant of the MVC pattern you are using) observes the data in the model and updates the UI components accordingly when the data changes. 视图(或控制器,取决于您使用的MVC模式的变体)观察模型中的数据,并在数据更改时相应地更新UI组件。

JavaFX makes it pretty easy to do this by defining a properties and binding API . JavaFX通过定义属性和绑定API使其非常容易。 These properties classes are directly observable, firing events to ChangeListener s when they change, and the binding API allows you to express dependencies between variables. 这些属性类是直接可观察到的,在它们发生更改时将事件触发到ChangeListener ,并且绑定API允许您表达变量之间的依赖关系。 The UI components themselves are written using these properties. UI组件本身是使用这些属性编写的。

So you can implement your model as 因此,您可以将模型实现为

public class Cell {
    private final BooleanProperty status;

    public Cell()  {
        System.out.println("DEAD CELL CREATED");

        status = new SimpleBooleanProperty(false);
    }

    public BooleanProperty statusProperty() {
        return status ;
    }

    public final boolean getStatus()  {
            return statusProperty().get();
    }

    public final void setStatus(boolean status)  {
        statusProperty().set(status);
    }

}

Then all you need to do is update the color of your rectangles when the corresponding cell's status changes: 然后,您所要做的就是在相应单元格的状态更改时更新矩形的颜色:

public Pane makeGridPane(Cell[][] grid) {
    Pane gridPane = new Pane();

    for(int i=0; i<gridSize; i++){
        for(int j=0; j<gridSize; j++){
            Rectangle rect = new Rectangle();

            System.out.println("grid[" + i + "][" + j +"]");

            if (grid[i][j].getStatus()) {
                rect.setFill(ALIVE_COLOR);
            } else {
                rect.setFill(DEAD_COLOR);
            }

            grid[i][j].statusProperty().addListener((obs, oldStatus, newStatus) -> {
                if (newStatus) {
                    rect.setFill(ALIVE_COLOR);
                } else {
                    rect.setFill(DEAD_COLOR);
                }
            });

            rect.setStroke(Color.BLACK);

            rect.setX(i * gridSize);
            rect.setY(j * gridSize);
            rect.setWidth(gridSize);
            rect.setHeight(gridSize);


            rect.setOnMouseClicked(new EventHandler<MouseEvent>(){
                @Override
                public void handle(MouseEvent me){
                        rect.setFill(Color.RED);
                }
            });

            gridPane.getChildren().add(rect);


        }
    }

    return gridPane;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM