Использование 2D-массивов с JavaFX и Scene Builder - PullRequest
0 голосов
/ 24 апреля 2019

Я создал программу на Java и пытаюсь создать для нее пользовательский интерфейс с помощью JavaFX. JavaFX поставляется с программным обеспечением Scene Builder, которое позволяет редактировать файл FXML, на который ссылается Java для создания макета пользовательского интерфейса.

В Scene Builder я создал серию круговых форм. Вот упрощенная версия файла FXML с 4 кружками:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.shape.Circle?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Circle fx:id="nCircle_0_0" fill="DODGERBLUE" layoutX="100.0" layoutY="120.0" onMouseClicked="#btnClick" onMouseEntered="#circleHover" onMouseExited="#circleLeave" radius="20.0" stroke="BLACK" strokeType="INSIDE" />
      <Circle fx:id="nCircle_0_1" fill="DODGERBLUE" layoutX="150.0" layoutY="120.0" onMouseClicked="#btnClick2" onMouseEntered="#circleHover" onMouseExited="#circleLeave" radius="20.0" stroke="BLACK" strokeType="INSIDE" />
      <Circle fx:id="nCircle_0_2" fill="DODGERBLUE" layoutX="200.0" layoutY="120.0" onMouseEntered="#circleHover" onMouseExited="#circleLeave" radius="20.0" stroke="BLACK" strokeType="INSIDE" />
      <Circle fx:id="nCircle_0_3" fill="DODGERBLUE" layoutX="250.0" layoutY="120.0" onMouseEntered="#circleHover" onMouseExited="#circleLeave" radius="20.0" stroke="BLACK" strokeType="INSIDE" />
   </children>
</AnchorPane>

Чтобы контролировать внешний вид этих круговых форм, мне нужно создать переменные, ссылающиеся на них, в моем файле Controller.Java. Я также поместил имена переменных в 2D-массив, потому что мне нужно ссылаться на них из других 2D-массивов в моей Java-программе:

package sample;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

public class Controller {

    @FXML
    public Button btn;
    public Circle nCircle_0_0, nCircle_0_1, nCircle_0_2, nCircle_0_3;

    public Circle theCircles []  = {nCircle_0_0, nCircle_0_1, nCircle_0_2, nCircle_0_3};
    public Circle [][] circleGrid = new Circle[2][2];

    // get method for circleGrid
    public Circle [][] getcircleGrid(){
        return circleGrid;
    }

    public void makeCircleGrid(){
        int count = 0;
        for (int row = 0; row < 2; row++){
            for (int col = 0; col < 2; col++){
                //theCircles[count] = new Circle();
                circleGrid[row][col] = theCircles[count];
                count += 1;
            }
        }
    }

    protected void printCirclesGrid(Circle [][] theGrid){
        for (int row =0; row < 2; row++){
            System.out.print(row);
            for (int col = 0; col < 2; col++){
                System.out.print("\t" + theGrid [row][col]);
            }
            System.out.println();
        }

    }

    public void circleHover(MouseEvent event){
        Circle value = (Circle)event.getSource();
        value.setStrokeWidth(4);
    }

    public void circleLeave(MouseEvent event){
        Circle value = (Circle)event.getSource();
        value.setStrokeWidth(1);
    }

    @FXML
    public void btnClick(MouseEvent event)    {
        nCircle_0_3.setFill(Color.web("#ed4b00"));

    }

    @FXML
    public void btnClick2(MouseEvent event)    {
        circleGrid[1][0].setFill(Color.web("#ed4b00"));
    }

}

Существует также файл Main.java, который запускает JavaFX и загружается в файл FXML:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();

        Controller startUp = new Controller();
        startUp.makeCircleGrid();
        startUp.printCirclesGrid(startUp.getcircleGrid());
    }

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

Проблема, с которой я столкнулся, заключается в том, что мне нужно хранить ссылки на круги в 2D-массиве, потому что мне нужно ссылаться на них и управлять ими из других мест в моей Java-программе. Я попытался поместить все имена переменных в двумерный массив, но Java видит их как нулевые.

Если вы запустите три файла, перечисленных выше, появится окно с 4 синими кружками. При нажатии на первый круг последний круг станет красным. Однако нажатие на второй круг приведет к ошибке java.lang.NullPointerException. (Если бы это сработало так, как я надеялся, он бы стал вторым последним красным кругом).

Мне сказали, что мне нужно инициализировать переменную Circle, сделав ее равной экземпляру объекта Circle перед помещением его в 2D-массив. Это позволит обойти проблему нулевых значений в двумерном массиве. Но я не вижу, как я могу инициализировать значения Circle в файле Controller.java, потому что объекты Circle были созданы в файле FXML, а не в файле Controller.java. Можно ли как-то хранить и ссылаться на объекты, созданные в файле FXML?

Ответы [ 2 ]

3 голосов
/ 24 апреля 2019

Если вы не пропустили какую-либо важную часть информации, похоже, ваши переменные никогда не инициализируются.

public Circle nCircle_0_0, nCircle_0_1, nCircle_0_2, nCircle_0_3, nCircle_0_4, nCircle_1_0, nCircle_1_1, nCircle_1_2, nCircle_1_3, nCircle_1_4;

Это не создаст никакого Круга, оно просто определяет переменную, которая инициализируется null. Вам нужно создать экземпляр и назначить его.

Пока вы не сделаете что-то вроде nCircle_0_0 = new Circle();, nCircle_0_0 будет null, поэтому вы получите NullPointerException.

Но самое главное, в Java вы работаете со ссылками, а не с «именами переменных» (если мы не говорим об отражении, что здесь не так). Это означает, что когда вы назначаете ссылку на массив, а затем назначаете другой экземпляр вашему nCircle_0_0, объект в массиве не будет затронут. Вы должны прочитать (и понять) это: https://stackoverflow.com/a/40523/2266098

По какой причине вы хотите определить переменную для каждого элемента в массиве? Почему вы не можете просто создать экземпляры в вашем цикле:

for (int row = 0; row < 5; row++){
    for (int col = 0; col < 5; col++){
        circleGrid[row][col] = new Circle();
    }
}

Вы не сможете получить значение через переменную nCircle_0_0, но вы все равно используете circleGrid[0][0].

0 голосов
/ 29 апреля 2019

Я обнаружил, в чем проблема:

Я поместил инструкции в файл Main.java:

        Controller startUp = new Controller();
        startUp.makeCircleGrid();
        startUp.printCirclesGrid(startUp.getcircleGrid());

Из того, что я вижу, это создает другой экземпляр контроллераучебный класс.Это не тот экземпляр, на который ссылается файл FXML.В результате значения nCircle_0_0 и nCircle_0_1 и т. Д. При входе в массив рассматриваются как нулевые.

Если вместо этого я вызываю метод makeCircleGrid ();от одной из кнопок или фигур, созданных в моем файле FXML, тогда полная ссылка на объект переходит в массив.Таким образом, он видит значения nCircle_0_0 и nCircle_0_1 и т. Д. Как фактические объекты Circle из файла FXML.Похоже, что экземпляр класса Controller, с которым общаются из файла FXML, отличается от экземпляра класса Controller, с которым я пытался общаться из Main.java

...