-1

I have following Code

TTTControl class:

public class TTTControl extends Application implements EventHandler<ActionEvent> {

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

    @Override
    public void start(Stage stage) throws Exception {
        String resource = "TTTGui.fxml";
        Parent root = FXMLLoader.load(getClass().getResource(resource));

        int window_width = 800;
        int window_height = 380;
        stage.setScene(new Scene(root, window_width, window_height));
        stage.show();
    }

    @Override
    public void handle(ActionEvent event) {
        // Könnte man ggfs. in der Zukunft noch benutzen
        System.out.println("Event!");
    }
}

Controller class:

public class Controller {
    int n = 3;
    Button buttons[][] = new Button[n][n];

    EventManager em = new EventManager();

    Controller() throws Exception {
        String resource = "TTTGui.fxml";
        Parent root = FXMLLoader.load(getClass().getResource(resource));

        for (int i=0; i < this.n; i++) {
            for (int j=0; j < this.n; j++) {
                String row = Integer.toString(i);
                String col = Integer.toString(j);
                Button btn = (Button) root.lookup("#"+row+col);
                btn.setStyle("-fx-base: white;");
                this.buttons[i][j] = btn;
            }
        }
    }

    // Funktion die beim Klicken eines Buttons mit dem onAction="#buttonPressed" Attribut ausgeführt wird
    public void buttonPressed(ActionEvent event) {

        if (((Button) event.getSource()).getStyle() == "-fx-base: green;" ||
            ((Button) event.getSource()).getStyle() == "-fx-base: red;") {
            // der Vollständigkeit halber wird hier auch noch ausgegeben, dass es zwar ein ClickEvent gab, aber das Feld schon belegt war
            System.out.println("Spieler 1 hat " + ((Button) event.getSource()).getId() + " gedrückt.");
            System.out.println("Dieses Feld ist jedoch schon besetzt.");
        } else {
            if (em.getActivePlayer() == "One") {
                System.out.println("Spieler 1 hat " + ((Button) event.getSource()).getId() + " gedrückt.");
                ((Button) event.getSource()).setStyle("-fx-base: green;");
                em.setActivePlayer("Two");

                if (em.checkWin("-fx-base: green;", this.buttons) == true) {
                    System.out.println("Spieler Eins hat gewonnen!");
                    neuesSpiel();
                }
            } else if (em.getActivePlayer() == "Two") {
                System.out.println("Spieler 1 hat " + ((Button) event.getSource()).getId() + " gedrückt.");
                ((Button) event.getSource()).setStyle("-fx-base: red;");
                em.setActivePlayer("One");

                if (em.checkWin("-fx-base: red;", this.buttons) == true) {
                    System.out.println("Spieler Zwei hat gewonnen!");
                    neuesSpiel();
                }
            }
        }
    }
}

EventManager:

public class EventManager {
    private String activePlayer;

    // Konstruktur von der Klasse mit ein paar Variablen die initialisiert werden
    EventManager() {
        this.activePlayer = "One";
    }

    public boolean checkWin(String color, Button[][] buttons) {
        // [Reihe][Spalte]
        // horizontal
        if (buttons[0][0].getStyle() == color && buttons[0][1].getStyle() == color && buttons[0][2].getStyle() == color) {
            return true;
        }
        else if (buttons[1][0].getStyle() == color && buttons[1][1].getStyle() == color && buttons[1][2].getStyle() == color) {
            return true;
        }
        else if (buttons[2][0].getStyle() == color && buttons[2][1].getStyle() == color && buttons[2][2].getStyle() == color) {
            return true;
        }
        // vertikal
        else if (buttons[0][0].getStyle() == color && buttons[1][0].getStyle() == color && buttons[2][0].getStyle() == color) {
            return true;
        }
        else if (buttons[0][1].getStyle() == color && buttons[1][1].getStyle() == color && buttons[2][1].getStyle() == color) {
            return true;
        }
        else if (buttons[0][2].getStyle() == color && buttons[1][2].getStyle() == color && buttons[2][2].getStyle() == color) {
            return true;
        }
        // diagonal
        else if (buttons[0][0].getStyle() == color && buttons[1][1].getStyle() == color && buttons[2][2].getStyle() == color) {
            return true;
        }
        else if (buttons[0][2].getStyle() == color && buttons[1][1].getStyle() == color && buttons[2][0].getStyle() == color) {
            return true;
        }
        return false;
    }

    // Getter und Setter Methoden für die private Variablen
    public void setActivePlayer(String activePlayer) {
        this.activePlayer = activePlayer;
    }

    public String getActivePlayer() {
        return this.activePlayer;
    }
}

So, I tried to create a controller class which should be handling all the button events for instance (I guess that is the controllers purpose in this case). Since I need the Parent root I guessed that inject(ing) the root into the controller means loading it once again at that place.... Buuut this does not work and I get following

Error:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
    at java.lang.Thread.run(Unknown Source)
Caused by: javafx.fxml.LoadException: 
/C:/Users/Ibrahim/git/nicolle_informatik/bin/vl06/TTTGui.fxml:21

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:103)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:934)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
    at vl06.TTTControl.start(TTTControl.java:20)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    ... 1 more
Caused by: java.lang.IllegalAccessException: Class sun.reflect.misc.ReflectUtil can not access a member of class vl06.Controller with modifiers ""
    at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
    at java.lang.Class.newInstance(Unknown Source)
    at sun.reflect.misc.ReflectUtil.newInstance(Unknown Source)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:927)
    ... 22 more
Exception running application vl06.TTTControl
BoJack Horseman
  • 4,406
  • 13
  • 38
  • 70
  • The elements of the `buttons` array are not initialized in the controller. They are only initialized in the instance of `TTTControl` created by `Application.launch()`. Don't use the application class as the controller class: it is too confusing to keep track of what is initialized and what is not initialized. Use a different class for the controller. – James_D May 23 '17 at 17:24
  • @James_D But how shall my new controller know of all the buttons? How can I get all the button objects in my controller class, that I used to have in my TTTControl class? – BoJack Horseman May 23 '17 at 18:12
  • Just define them in the controller... – James_D May 23 '17 at 18:14
  • But how can I access the Parent root in my controller? Edit: I mean this resource `FXMLLoader.load(getClass().getResource(resource));`. Shall I just load it again? seems to be a little redundant to do so – BoJack Horseman May 23 '17 at 18:14
  • Just inject it into the controller. (If you load it again, you get a new instance of it.) Though: why do you need it in the controller? You only have it as a local variable in `start()` and the only thing you do with it there is set it in the scene anyway. – James_D May 23 '17 at 18:19
  • I need the root for this **for loop** `for (int i=0; i < this.n; i++) { for (int j=0; j < this.n; j++) { String row = Integer.toString(i); String col = Integer.toString(j); Button btn = (Button) root.lookup("#"+row+col); btn.setStyle("-fx-base: white;"); this.buttons[i][j] = btn; } }` So I can store the Button objects in the array – BoJack Horseman May 23 '17 at 18:22
  • OK, yes; I missed that. So just inject the root into the controller, as you would with any other element defined in the FXML file. – James_D May 23 '17 at 18:23
  • @James_D may I update my question and upload my new attempt? Because I still can't really get it working – BoJack Horseman May 23 '17 at 18:34
  • Yes, of course. – James_D May 23 '17 at 18:34
  • @James_D it's updated now, I have the new controller class – BoJack Horseman May 23 '17 at 18:43
  • OK, I'm not meaning to be patronizing here, but have you actually read any tutorials, or *anything* on using FXML and controllers? Where are your injected fields? Where is your `initialize()` method? Maybe start [here](https://stackoverflow.com/documentation/javafx/1580/fxml-and-controllers/5125/example-fxml#t=201705231846226879343)? I'll provide an answer, but you are really expected to do some minimal research on how to use the technology you are using before posting here. – James_D May 23 '17 at 18:47

1 Answers1

1

The exception arises because the constructor in your controller is not public.

There are many other errors in your code.

You are loading a second copy of the UI defined in the FXML in the controller:

Controller() throws Exception {
    String resource = "TTTGui.fxml";
    Parent root = FXMLLoader.load(getClass().getResource(resource));

    // ...
}

This creates a second instance of the root element of the FXML file, along with new instances of everything contained in it. Obviously, this is not the same instance as the one you displayed in the Scene in your start method. So the buttons are different buttons to the ones displayed, and the configuration on them will have no effect.

You need to inject the root element into the controller using the usual fx:id attribute in the FXML, and using the @FXML annotation:

public class Controller {
    int n = 3;
    Button buttons[][] = new Button[n][n];

    EventManager em = new EventManager();

    @FXML
    private Parent root ;

   // ...
}

and then add fx:id="root" to the root element of the FXML file.

Additionally, you are trying to reference elements of the FXML in the controller's constructor, instead of the initialize() method. As describe here, the constructor is necessarily invoked before the elements defined in the FXML can be injected, so they will not be accessible there.

So you need something like

public class Controller {

    int n = 3;
    Button buttons[][] = new Button[n][n];

    EventManager em = new EventManager();

    @FXML
    private Parent root ;

    public void initialize() {

        for (int i=0; i < this.n; i++) {
            for (int j=0; j < this.n; j++) {
                String row = Integer.toString(i);
                String col = Integer.toString(j);
                Button btn = (Button) root.lookup("#"+row+col);
                btn.setStyle("-fx-base: white;");
                this.buttons[i][j] = btn;
            }
        }
    }

   // ...
}

Finally, you have some basic Java errors which are beyond the scope of this question. E.g. you should read How do I compare strings in Java?

James_D
  • 201,275
  • 16
  • 291
  • 322