JavaFX показывает правильное значение в координатах кнопок

#javafx #fxml #actionevent

#javafx #fxml #actionevent

Вопрос:

Я свел вопрос только к проблеме, поэтому я создаю пользовательский интерфейс для игры в сапер, в контроллере ниже, в методе startGame, все хорошо (согласно моей проверке с использованием debug), пока строка

game.open(x, y)

, то есть мне удается создать доску и добавить кнопки (которые будут выступать в качестве слотов) в панель сетки, но моя проблема в том, что я не могу открыть и показать правильное значение в слоте. (класс logics of Mines работает нормально — тот, который не содержит части JavaFX).

 package MS;

import java.io.IOException;
import java.util.Random;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class NewGameCONTROLLER {
    Mines game;
    int rows, columns, mines;
    Button b;

    @FXML
    private TextField NumRows;

    @FXML
    private TextField NumCols;

    @FXML
    private TextField NumM;

    @FXML
    private Button StartGame;

    @FXML
    private Button BackMainMenu;

    @FXML
    private Button RandomGame;

    @FXML
    void BackMainMenu(ActionEvent event) throws IOException {
        FXMLLoader loader = new FXMLLoader(); // Create loading object
        loader.setLocation(getClass().getResource("MainMenuFXML.fxml")); // fxml location
        VBox root = loader.load(); // Load layout
        root.setStyle("-fx-background-image: url("file:///C:/EclipseProjects/MineSweeper/src/MS/menu.jpg")");
        Scene scene = new Scene(root); // Create scene with chosen layout
        Stage primaryStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        primaryStage.setTitle("..."); // Set stage's title
        primaryStage.setMinWidth(400); // Won't be allowed to make width/height smaller
        primaryStage.setMinHeight(350);
        primaryStage.setMaxWidth(600);
        primaryStage.setMaxHeight(450);
        primaryStage.setScene(scene); // Set scene to stage
        primaryStage.show(); // Show stage
    }

    @FXML
    void RandomGame(ActionEvent event) {
        Random rand = new Random();
        int low = 3, high = 15;
        int minesLow = 1, minesHigh;
        rows = rand.nextInt(high - low)   low;
        columns = rand.nextInt(high - low)   low;
        minesHigh = rows * columns - 1;
        mines = rand.nextInt(minesHigh - minesLow)   minesLow;
        game = new Mines(rows, columns, mines);
    }

    @FXML
    void StartGame(ActionEvent event) throws IOException {
        rows = Integer.parseInt(NumRows.getText());
        columns = Integer.parseInt(NumCols.getText());
        mines = Integer.parseInt(NumM.getText());
        game = new Mines(rows, columns, mines);

        FXMLLoader loader = new FXMLLoader(); // Create loading object
        loader.setLocation(getClass().getResource("BoardFXML.fxml")); // fxml location

        AnchorPane root = loader.load(); // Load layout
        root.setStyle("-fx-background-image: url("file:///C:/EclipseProjects/MineSweeper/src/MS/Pic.jpg")");
        Scene scene = new Scene(root); // Create scene with chosen layout
        Stage gameStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        gameStage.setTitle("..."); // Set stage's title
        gameStage.setMinWidth(1000); // Won't be allowed to make width/height smaller
        gameStage.setMinHeight(500);
        gameStage.setMaxWidth(1200);
        gameStage.setMaxHeight(800);
        gameStage.setScene(scene); // Set scene to stage

        BoardCONTROLLER bCont = loader.getController(); // Prepare board in BoardCONTROLLER
        for (int i = 0; i < columns; i  )
            for (int j = 0; j < rows; j  ) {
                b = new Button(" ");
                b.setMinSize(40, 40);
                b.setMaxSize(40, 40);
                bCont.TheBoard.add(b, i, j);
            }
        for (int i = 0; i < bCont.TheBoard.getChildren().size(); i  ) {
            ((ButtonBase) bCont.TheBoard.getChildren().get(i)).setOnAction(new EventHandler<ActionEvent>() {

                @Override
                public void handle(ActionEvent event) {
                    int x, y;
                    event.getSource();
                    x = (int) ((Button) event.getSource()).getProperties().get("gridpane-column");
                    y = (int) ((Button) event.getSource()).getProperties().get("gridpane-row");
                    game.open(x, y);
                    for (int i=0;i<game.getCol();i  )
                        for (int j=0;i<game.getRow();j  ) {
                            if (game.board[i][j].charAt(1)=='T')
                                ((Button) bCont.TheBoard.getChildren().get(i)).setText(game.get(x, y));
                        }
                /*  while ()
                    for (int i = 0; i < bCont.TheBoard.getChildren().size(); i  ) {
                        if (game.board[x][y].charAt(2) == 'B') {
                            ((Button) bCont.TheBoard.getChildren().get(i)).setText(game.get(x, y));
                        }
                    }*/
                }
            });
        }
        gameStage.show(); // Show stage
    }

}
  

Спасибо.

Комментарии:

1. @matt готово, надеюсь, теперь это более понятно

2. Круто, это намного лучше. Похоже, вы нажимаете кнопку, и вам нужно проверить, есть ли у этой кнопки бомба, и «открыть» ее, если ее нет. Возвращаются ли правильные значения для x и y?

3. @matt да, значения x и y возвращаются правильно (убедился и проверил это в отладчике), переменная «game» (объект Mines) открывает правильный слот (после «game.open (x, y)»), метод «open» в основном добавляет символ «T», поэтому код открывает этот слот («T» — это индикатор открываемого слота). И теперь, что касается проблемы, я, похоже, не могу найти способ ее открыть, я пытался, как показано в цикле, но это неправильно, поскольку этот слот уже содержит «T», поэтому он открывает много других слотов (которые не следует открывать), мне нужно открыть только правильный слот (содержащий «T»).

4. Почему вы просматриваете все слоты? Разве у вас еще нет слота, который вы хотите изменить? (Button)evt.getSource() это кнопка, которую вы хотите изменить?

5. пожалуйста, соглашения об именовании Java

Ответ №1:

Когда я смотрю на этот цикл.

 for (int i=0;i<game.getCol();i  )
    for (int j=0;i<game.getRow();j  ) {
        if (game.board[i][j].charAt(1)=='T')
            ((Button) bCont.TheBoard.getChildren().get(i)).setText(game.get(x, y));
    }    
}
  

Переменная i не соответствует координате строки, она соответствует n-му дочернему элементу в таблице. Так что, на самом деле, вы просто перебираете все дочерние элементы в первой строке (или первом столбце).

Я вижу, как это исправить;

 for( Node child: bCont.TheBoard.getChildren() ){
    int i = (int) ((Button) child).getProperties().get("gridpane-column");
    int j = (int) ((Button) child).getProperties().get("gridpane-row");
    if (game.board[i][j].charAt(1)=='T'){
        ((Button) child).setText(game.get(x, y));
    }
}
  

Таким образом, вы просматриваете всех дочерних элементов и проверяете их статус на доске.

Я не знаю, соответствует ли порядок дочерних узлов в GridPane порядку компоновки, распространенному методу упаковки 2D-массива в 1D-массив,

 bCont.TheBoard.getChildren().get(i   j*game.getCol());
  

Помните, что у вас всего кнопок в строке x.

Комментарии:

1. Я просто изменил строку if на game.get(i, j) вместо (x, y), и это отлично сработало (я предполагаю, что это потому, что если в слоте просто есть «T», он должен быть открыт)! Спасибо! Также ваше последнее замечание, я знаю это, но никогда не пытался реализовать его таким образом, очень полезно, спасибо!