Kann nicht auf GUI Elemente zugreifen, mit einer Methode im Controller klappts, mit der anderen nich

kevinistes

Mitglied
Hallo Community,

ich habe einen Fehler in meinem Quelltext doch ich komme der Ursache nicht auf den Grund.

Zur zeit entwickeln wir in Projektarbeit für die Uni ein Spiel. Zurzeit sind wir noch beim Erstellen des Menüs und der config.
Ich habe eine .fxml Datei erstellt und versuche mit dem Controller: "Controller.java" darauf zuzugreifen.

Die methode save_settings funktionert, ich kann die Daten aus den Feldern auslesen und mithilfe einer Methode der Klasse Settings.java speichern, jedoch in der Methode Controller.load_settings bekomme ich immer Nullpointer Exceptions, obwohl die Methode die selben Eigenschaften besitzt, in der selben Klasse steht und die scene-controls eigentlich auch auslesen können sollte.

Ich hab schon Teile auskommentiert um den Fehler besser eingrenzen zu können, jedoch ein simples setText(); funktioniert nichtmal.

(Die Klasse Settings funktioniert einwandfrei, da kann der Fehler definitiv nicht liegen)

Hier ist der Ausschnitt meines Quelltexts:

public javafx.scene.control.Button btn_exit;
public javafx.scene.control.Button btn_settings_saveexit;
public javafx.scene.control.Button btn_settings_apply;
public javafx.scene.control.Slider sl_settings_audiovolume;
public javafx.scene.control.TextField tf_settings_name;
public javafx.scene.control.ChoiceBox cb_settings_resolution;
public javafx.scene.control.ChoiceBox cb_settings_language;

public void settings(Event event) throws Exception{
Parent info = FXMLLoader.load(getClass().getResource("settings.fxml"));
Stage settings_stage = new Stage();
settings_stage.setTitle("Settings");
settings_stage.setScene(new Scene(info, 650, 400));
settings_stage.show();
load_settings();
}

public void
save_settings(){ //Diese Methode funktioniert

Settings cfg = new Settings();
//cfg.load();
cfg.name = tf_settings_name.getText();
cfg.resolution = cb_settings_resolution.getValue().toString();
cfg.getresolutionvalues(cfg.resolution);
cfg.audiovolume = sl_settings_audiovolume.getValue();
sl_settings_audiovolume.setValue(0.5);
cfg.language = cb_settings_language.getValue().toString();
cfg.save();
System.out.println("Methode save_settings durch");
}

public void load_settings(){ //Bisher kein Zugriff auf die Java scene controls
Settings cfg = new Settings();
cfg.load();
tf_settings_name.setText(cfg.getString("name")); //Auch ein simples .setText("Test"); geht nicht
// Hier bekomme ich die Nullpointer Exception

//tf_settings_name.setText("Test");
//cb_settings_resolution.setValue(cfg.getString(cfg.resolution));
//sl_settings_audiovolume.setValue(cfg.getDouble("audiovolume"));
//sl_settings_audiovolume.setValue(0.5);
// cb_settings_language.setValue(cfg.getString(cfg.language));
System.out.println("Methode load_settings durch");
}


Wäre toll wenn mir jemand in absehbarer Zeit helfen könnte und meinen Fehler findet.

Viele Grüße,
kevinistes
 
Zuletzt bearbeitet:
Wie ich aus deinem Titel lesen kann, klappen Methoden aus dem Controller und aus einer anderen Klasse nicht?! Ist doch klar!
Deine UI Elemente sind im Controller gebunden.

Schreibe Methoden in deiner Controllerklasse, lade den Controller und dann klappts auch mit dem holen/setzen des Textes.

Java:
FXMLLoader loader = new FXMLLoader(/*getClass()...*/);
Parent p = loader.load();
MyController controller = loader.getController();
//Methode zum holen des Textes aus einem TextField
controller.getTextFromTextField();
//controller.setTextToTextField("....");

et voilà
 
Hi, Danke für deine Antwort. ich glaube du hast mich falsch verstanden oder ich hab es zu ungenau beschrieben.
Also der Quelltext den ich gepostet habe ist teil derselben Controller-Klasse.

In dieser Klasse befinden sich die geposteten Methoden.
Die Methode save_settings funktioniert und tut was sie soll (nämlich speichern der Einstellungen). Die Methode load_settings steht ebenfalls im Controller und soll nach aufruf des Fensters: settings.fxml, die Felder so setzen wie es in einer config gespeichert wurde.
Dh, in der Config steht zb der Name: Peter, Peter soll dann auch direkt beim aufruf des Fensters Settings in das textfeld tf_settings_name geschrieben werden, dasselbe gilt für die anderen scene controls

Jedoch bekomme ich immer eine Nullpointer Exception beim setzen des Textes, obwohl es halt wie gesagt in einem Controller geschieht nur in verscheidenen Methoden... Wir betrachten hier eine Klasse (Controller.java) und eine layout datei: settings.fxml

Danke 🙂
 
Poste mal die Klasse Settings.


Generell habe ich die Befürchtung, dass das Instanzieren der Klasse Settings jeweils in den zwei Methoden save/load den Fehler inne hält.

Gesetz dem Fall die Klasse Settings besitzt keine default Werte, woher soll dann die Instanz in der load Methode seine infos her holen? Und selbst dann wenn du die save Methode als erstes aufrufst und die Klassen members von Settings beschreibst, dann nach Aufruf der load Methode eine komplett neue Instanz erstellst.

Setzt aber voraus dass du in der Klasse Settings nix persistent abspeicherst.


EDIT 2: Vergiss meinen Kommentar. Sehe gerade du hast schon mit .setText("Test"); versucht.
ist die Methoden load Settings denn auch in der FXML vorhanden oder korrekt geschrieben?
 
Zuletzt bearbeitet:
Ich würd mal vorschlagen du postest mal den ganzen Code, denn so ist nichts ersichtlich, was falsch ist oder nicht funktioniert.
 
Ich denke, wenn du Flowns initialen Post folgst, und dort statt controller.getTextFromTextField(); eben load_settings aufrufst, dann sollte es klappen. Ich denke das hier einfach die UI noch nicht so weit ist und mit Flowns Variante sollte es dagen noch initialisiert werden.

Eine Bemerkung noch: Ich weiss ja nicht, was config.load() macht, aber wenn es z.B. DB-Operationen sind, sollten diese in einem eigenen Thread laufen. JavaFX hat einen UI-Thread und es ist - gelinde gesagt - bad practice auf diesem zu arbeiten. Extende die JavaFX-Klasse Task und höre auf das Beenden des Tasks.

Ach und noch etwas: "load_settings" ist nicht wirklich Java-konform. Auch wenn es Java am Ende egal ist, solltest du die Methodennamen camelCase machen, also "loadSettings". Gilt auch für Variablen etc. Such mal nach Code Conventions...
 
Danke für eure antworten, (@Hades) wie schon beschrieben, an der Settings-Klasse liegt es definitv nicht. Sie funktioniert einwandfrei und speichert auch default settings. Da ich in der load_settings methode das getten der Informationen von der Settings-Klasse auskommentiert habe und dies durch ein simples set Text ersetzt habe, spielt die Klasse momentan auch keine Rolle.

Controller.java:
Java:
package de.hhu.propra;

import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;


import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
import java.util.ResourceBundle;
import java.net.URL;


public class Controller implements Initializable {

    public javafx.scene.control.Button btn_exit;

    public javafx.scene.control.Button btn_settings_saveexit;
    public javafx.scene.control.Button btn_settings_apply;

    public javafx.scene.control.Slider sl_settings_audiovolume;
    public javafx.scene.control.TextField tf_settings_name;
    public javafx.scene.control.ChoiceBox cb_settings_resolution;
    public javafx.scene.control.ChoiceBox cb_settings_language;

    public void showinfo(Event event) throws Exception{
        Parent info = FXMLLoader.load(getClass().getResource("info.fxml"));
        Stage info_stage = new Stage();
        info_stage.setTitle("Info");
        info_stage.setScene(new Scene(info, 600, 200));
        info_stage.show();
        }

    public void settings(Event event) throws Exception{
        Parent info = FXMLLoader.load(getClass().getResource("settings.fxml"));
        FXMLLoader loader = new FXMLLoader(getClass().getResource("settings.fxml"));
        loader.setController(this);
        Stage settings_stage = new Stage();
        settings_stage.setTitle("Settings");
        settings_stage.setScene(new Scene(info, 650, 400));
        settings_stage.show();
        load_settings(); // Aufruf der Fehlerhaften Methode!

    }
    public void save_settings(){

        Settings cfg = new Settings();
        //cfg.load();
        cfg.name = tf_settings_name.getText();
        cfg.resolution = cb_settings_resolution.getValue().toString();
        cfg.getresolutionvalues(cfg.resolution);
        cfg.audiovolume = sl_settings_audiovolume.getValue();
        sl_settings_audiovolume.setValue(0.5);
        cfg.language = cb_settings_language.getValue().toString();
        cfg.save();
        System.out.println("Methode save_settings durch");
    }
    public void load_settings(){                    //Bisher kein Zugriff auf die Java scene controls

        Settings cfg = new Settings();
        cfg.load();
        //tf_settings_name.setText(cfg.getString("name"));
        tf_settings_name.setText("Test");    //<- Hier gibts die Nullpointer Exception
        //cb_settings_resolution.setValue(cfg.getString(cfg.resolution));
        //sl_settings_audiovolume.setValue(cfg.getDouble("audiovolume"));
        //sl_settings_audiovolume.setValue(0.5);
        // cb_settings_language.setValue(cfg.getString(cfg.language));
        System.out.println("Methode load_settings durch");

    }
}

Das fxml sheet dazu sieht so aus:

<?xml version="1.0" encoding="UTF-8"?>
<?importjava.net.*?>
<?importjavafx.geometry.*?>
<?importjavafx.scene.control.*?>
<?importjava.lang.*?>
<?importjavafx.scene.layout.*?>
<?importjava.net.URL?>
<?importcom.sun.javafx.scene.control.skin.ContextMenuContent.ArrowMenuItem?>
<GridPane id="settings_gridpane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" styleClass="settings" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.hhu.propra.Controller">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="195.0" minWidth="10.0" prefWidth="144.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="311.0" minWidth="10.0" prefWidth="104.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="372.0" minWidth="10.0" prefWidth="344.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Name:" GridPane.columnIndex="1" GridPane.halignment="RIGHT">
<GridPane.margin>
<Insets right="10.0" />
</GridPane.margin>
</Label>
<Label text="Resolution:" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.rowIndex="1">
<GridPane.margin>
<Insets right="10.0" />
</GridPane.margin>
</Label>
<Label text="Audio Volume: " GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.rowIndex="3">
<GridPane.margin>
<Insets right="10.0" />
</GridPane.margin>
</Label>
<Label text="Language:" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
<GridPane.margin>
<Insets right="10.0" />
</GridPane.margin>
</Label>
<TextField fx:id="tf_settings_name" text="Player" GridPane.columnIndex="2">
<GridPane.margin>
<Insets left="10.0" right="10.0" />
</GridPane.margin>
</TextField>
<ChoiceBox fx:id="cb_settings_resolution" prefWidth="150.0" GridPane.columnIndex="2" GridPane.rowIndex="1" value="1280x720">
<String fx:value="800x600"/>
<String fx:value="1280x720"/>
<String fx:value="1920x1080"/>
<GridPane.margin>

<Insets left="10.0" />
</GridPane.margin>

</ChoiceBox>
<Slider fx:id="sl_settings_audiovolume" blockIncrement="0.05" majorTickUnit="0.5" max="1.0" minorTickCount="1" GridPane.columnIndex="2" GridPane.rowIndex="3">
<GridPane.margin>
<Insets left="10.0" right="10.0" />
</GridPane.margin>
</Slider>
<ChoiceBox fx:id="cb_settings_language" prefWidth="150.0" GridPane.columnIndex="2" GridPane.rowIndex="4" value="en">
<String fx:value="en"/>
<String fx:value="de"/>
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</ChoiceBox>
<Button fx:id="btn_exit" mnemonicParsing="false" onAction="#home" text="Exit without saving" GridPane.rowIndex="5">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Button>
<Button fx:id="btn_settings_apply" mnemonicParsing="false" onAction="#settings_apply" text="Apply" GridPane.columnIndex="2" GridPane.halignment="LEFT" GridPane.rowIndex="5">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Button>
<Button fx:id="btn_settings_saveexit" mnemonicParsing="false" onAction="#settings_saveandexit" text="Save and exit" GridPane.columnIndex="2" GridPane.halignment="RIGHT" GridPane.rowIndex="5">
<GridPane.margin>
<Insets right="15.0" />
</GridPane.margin>
</Button>
</children>
<stylesheets>
<URL value="@layout.css" />
</stylesheets>
</GridPane>

Danke schonmal im vorraus
 
Zuletzt bearbeitet:
Also Ich denke dein Problem könnte auch daher kommen, dass du die Controller-Instanz selbst im Controller-Code an eine andere UI hängst.
Was soll dass? Dein Code ist im Moment einfach nur unverständlich.

Der Controller muss ja zuvor schon einmal initialisiert worden sein, jetzt hängst du ihn einfach um und weisst ihn einer andere UI (ebenfalls) zu... Zumal du im FXML den Controller schon verlinkt hast! fx:controller="de.hhu.propra.Controller"

Was du machen müsstes, wäre wenn dann überhaupt
Java:
    public void settings(Event event) throws Exception{
        Parent info = FXMLLoader.load(getClass().getResource("settings.fxml"));
        FXMLLoader loader = new FXMLLoader(getClass().getResource("settings.fxml"));
        // BLÖDSINN: loader.setController(this);
        Stage settings_stage = new Stage();
        settings_stage.setTitle("Settings");
        settings_stage.setScene(new Scene(info, 650, 400));
        settings_stage.show();
        // WEG DAMIT: load_settings(); // BZW UMSCHREIBEN NACH
        ((de.hhu.propra.Controller) loader.getController()).load_settings();
    }

UND BITTE: Folge bitte den Java Code Conventions... #load_settings() --> #loadSettings()
 
Also Ich denke dein Problem könnte auch daher kommen, dass du die Controller-Instanz selbst im Controller-Code an eine andere UI hängst.
Was soll dass? Dein Code ist im Moment einfach nur unverständlich.

Der Controller muss ja zuvor schon einmal initialisiert worden sein, jetzt hängst du ihn einfach um und weisst ihn einer andere UI (ebenfalls) zu... Zumal du im FXML den Controller schon verlinkt hast! fx:controller="de.hhu.propra.Controller"

Was du machen müsstes, wäre wenn dann überhaupt
Java:
    public void settings(Event event) throws Exception{
        Parent info = FXMLLoader.load(getClass().getResource("settings.fxml"));
        FXMLLoader loader = new FXMLLoader(getClass().getResource("settings.fxml"));
        // BLÖDSINN: loader.setController(this);
        Stage settings_stage = new Stage();
        settings_stage.setTitle("Settings");
        settings_stage.setScene(new Scene(info, 650, 400));
        settings_stage.show();
        // WEG DAMIT: load_settings(); // BZW UMSCHREIBEN NACH
        ((de.hhu.propra.Controller) loader.getController()).load_settings();
    }

UND BITTE: Folge bitte den Java Code Conventions... #load_settings() --> #loadSettings()

Jo du hast recht, das ist unsinnig, ich habe nur rumprobiert woran der Fehler liegen könnte, jedoch erfolglos. Den code habe ich jetzt umgeändert nach den java conventions, ich werde jetzt nochmal das programm neu strukturieren und mir die Controller geschichte nochmal genauer angucken. Das ist für mich jetzt mein erstes JavaFX Projekt. Muss erstmal damit klarkommen 😀 war anfangs doch was unverständlich und ich habe dann einfach mal drauf los programmiert was im nachhinein nicht gerade clever war 😀 ... Dank dir für deine Hilfe ich kümmer mich mal drum und schau mal obs dann funktioniert.

Vielen Dank
 
Also ich habe jetzt zu jeder .fxml einen eigenen Controller gemacht. Dann habe ich die Java Code Conventions befolgt, und nochmal nach meinem Fehler gesucht. Ich habe auch deinen Vorschlag ausprobiert, jedoch erhalte ich weiterhin eine Nullpointer Exception. Meine Codes sehen jetzt so aus:

ControllerMain.java:
Java:
package de.hhu.propra;


import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;


public class ControllerMain {

    public javafx.scene.control.Button btn_exit;
    public javafx.scene.control.Button btn_launchgame;

    public void mainShowinfo(Event event) throws Exception{
        Parent info = FXMLLoader.load(getClass().getResource("info.fxml"));
        Stage info_stage = new Stage();
        info_stage.setTitle("Info");
        info_stage.setScene(new Scene(info, 600, 200));
        info_stage.show();
        }
    public void mainSettings(Event event) throws Exception{
        Parent info = FXMLLoader.load(getClass().getResource("settings.fxml"));
        FXMLLoader loader = new FXMLLoader(getClass().getResource("settings.fxml"));
        Stage settings_stage = new Stage();
        settings_stage.setTitle("Settings");
        settings_stage.setScene(new Scene(info, 650, 400));
        settings_stage.show();
        ((de.hhu.propra.ControllerSettings) loader.getController()).loadSettings(); //<<<Nullpointer Exception
    }
    public void mainLaunchgame(ActionEvent actionEvent) throws Exception{
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("gameoptions.fxml"));
        Pane gameoptions = (Pane) FXMLLoader.load(getClass().getResource("gameoptions.fxml"));
        Stage gameoptions_stage = new Stage();
        gameoptions_stage.setTitle("Launchgame");
        gameoptions_stage.setScene(new Scene(gameoptions));
        //info_stage.setScene(new Scene(root, 150, 50));
        gameoptions_stage.setResizable(false);
        gameoptions_stage.show();
    }

    public void exit(ActionEvent actionEvent) {
        System.exit(0);
    }

    public void home(ActionEvent actionEvent) {
        Stage stage = (Stage) btn_exit.getScene().getWindow();
        stage.close();
    }
}

ControllerSettings.java
Java:
package de.hhu.propra;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.stage.Stage;

public class ControllerSettings{

    public javafx.scene.control.Button btn_settings_saveexit;
    public javafx.scene.control.Button btn_settings_apply;

    public javafx.scene.control.Slider sl_settings_audiovolume;
    public javafx.scene.control.TextField tf_settings_name;
    public javafx.scene.control.ChoiceBox cb_settings_resolution;
    public javafx.scene.control.ChoiceBox cb_settings_language;

    public void saveSettings(){

        Settings cfg = new Settings();
        cfg.load();
        cfg.name = tf_settings_name.getText();
        cfg.resolution = cb_settings_resolution.getValue().toString();
        cfg.getresolutionvalues(cfg.resolution);
        cfg.audiovolume = sl_settings_audiovolume.getValue();
        sl_settings_audiovolume.setValue(0.5);
        cfg.language = cb_settings_language.getValue().toString();
        cfg.save();
        System.out.println("Methode saveSettings durch");
    }
    public void loadSettings(){                    //Bisher kein Zugriff auf die Java scene controls

        //Settings cfg = new Settings();
        //cfg.load();
        //tf_settings_name.setText(cfg.getString("name"));
        tf_settings_name.setText("Test");
        //cb_settings_resolution.setValue(cfg.getString(cfg.resolution));
        //sl_settings_audiovolume.setValue(cfg.getDouble("audiovolume"));
        //cb_settings_language.setValue(cfg.getString(cfg.language));
        System.out.println("Methode loadSettings durch");

    }
    public void settingsApply(ActionEvent actionEvent) {
        saveSettings();
    }
    public void settingsSaveandexit(ActionEvent actionEvent) {
        saveSettings();
        Stage stage = (Stage) btn_settings_saveexit.getScene().getWindow();
        stage.close();
    }
    public void home(ActionEvent actionEvent) throws Exception{
        Stage stage = (Stage) btn_settings_saveexit.getScene().getWindow();
        stage.close();
    }
}

in der fxml habe ich natürlich auch den Controller zugewiesen, und die Methodennamen der Buttons geändert... Ich hab so im Verdacht dass das aufrufen und setzen aus einer anderen Klasse nicht funktioniert. Die Methode saveSettings() rufe ich ja in der Klasse ControllerSettings auf, nämlich in der Methode settingsApply() die von dem Button "Apply" aufgerufen wird... dann werden die Einstellungen gespeichert... gibt es noch eine andere Möglichkeit beim Öffnen des Settings-Fensters die Felder zu setzen? Außer in der Klasse ControllerMain?
 
Zuletzt bearbeitet:
in der fxml habe ich natürlich auch den Controller zugewiesen, und die Methodennamen der Buttons geändert... Ich hab so im Verdacht dass das aufrufen und setzen aus einer anderen Klasse nicht funktioniert. Die Methode saveSettings() rufe ich ja in der Klasse ControllerSettings auf, nämlich in der Methode settingsApply() die von dem Button "Apply" aufgerufen wird... dann werden die Einstellungen gespeichert... gibt es noch eine andere Möglichkeit beim Öffnen des Settings-Fensters die Felder zu setzen? Außer in der Klasse ControllerMain?

Wenn ich die Settings aus der ControllerSettings.java Klasse lade funktioniert das setzen, nur halt nicht wenn ich das Fenster öffne :/
 

Zurück
Oben