Verschieden Scenen ansprechen mit dem Scene Builder und JavaFX (Eclipse)

DaveDarell

Mitglied
Hallo,

ich versuche mich aktuell daran, eine mehrstufige GUI mit dem Scene Builder zu bauen und habe aktuell ein Problem. Ich habe drei Scenen (Scene1.fxml, Scene2.fxml und Scene3.fxml) und drei Controller (Controller1, Controller2und Controller3). Folgendes soll möglich sein:

  • Scene 1 soll der Start sein
  • Von Scene 1 soll man auf Scene 2 und Scene 3 kommen
  • Von Scene 2 auf Scene 1 oder Scene 3
  • Von Scene 3 auf Scene 1

Meine Main.java sieht wie folgt aus:

Java:
public class Main extends Application {

    @Override
    public void start(Stage stage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("Scene1.fxml"));
        
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

Die Controllerklassen sind eigentlich (bisher) alle gleich aufgebaut, als Beispiel hier mal Controller1:

Java:
package application;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Controller1 implements Initializable {



    public void switchToScene2(ActionEvent event) throws IOException {

        
        
        Parent tableView = FXMLLoader.load(getClass().getClassLoader().getResource("Scene2.fxml"));
        Scene tableViewscene = new Scene(tableView);
        Stage window = (Stage)((Node)event.getSource()).getScene().getWindow();
        window.setScene(tableViewscene);
        window.show();
    }

    public void switchToScene3(ActionEvent event) throws IOException {

        Parent tableView = FXMLLoader.load(getClass().getClassLoader().getResource("Scene3.fxml"));
        Scene tableViewscene = new Scene(tableView);
        Stage window = (Stage)((Node)event.getSource()).getScene().getWindow();
        window.setScene(tableViewscene);
        window.show();
        
        
    }

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        // TODO Auto-generated method stub
        
    }
}



Ich weiß nicht wo genau da der Fehler liegt, ich habe schon verschieden Sachen ausprobiert in den Controllerklassen, alles bisher ohne Erfolg, es kommt eigentlich immer eine Fehlermeldung (Hänge mal eine Textdatei damit im Anhang an.)

Ich würde mich freuen wenn mir hier von euch jemand einen Tipp geben kann, wo genau mein Fehler liegt :)

Vorab schon mal vielen Dank!
 

Anhänge

  • Fehlermeldung.txt
    5,3 KB · Aufrufe: 1
K

kneitzel

Gast
Die fxml Datei wird nicht gefunden. Wo liegen die fxml Dateien? In welchem Namespace sind die Klassen? Gross-/Kleinschreibung bei den Namen beachtet?

Das getResource bei der Klasse schaut im Verzeichnis nach, das dem Namespace entspricht. Daher müsste Scene3.fxml im Verzeichnis application sein. Bei der Klasse Main ist kein package mit angegeben...
 

DaveDarell

Mitglied
Die fxml Datei wird nicht gefunden. Wo liegen die fxml Dateien? In welchem Namespace sind die Klassen? Gross-/Kleinschreibung bei den Namen beachtet?

Das getResource bei der Klasse schaut im Verzeichnis nach, das dem Namespace entspricht. Daher müsste Scene3.fxml im Verzeichnis application sein. Bei der Klasse Main ist kein package mit angegeben...

Also es liegt alles unter "application" ab und die Groß und Kleinschreibung sitzt eigentlich auch. Soll ich da mal ein extra Package machen für die FXML Files? Bzw. das ggf. aus "application" raus schieben und entsprechend anpassen?

Bei der Main wird "package application;" angegeben, habe ich leider nur vergessen in den Code einzufügen, das war mein Fehler...

1633638266733.png
 

DaveDarell

Mitglied
Also es liegt alles unter "application" ab und die Groß und Kleinschreibung sitzt eigentlich auch. Soll ich da mal ein extra Package machen für die FXML Files? Bzw. das ggf. aus "application" raus schieben und entsprechend anpassen?

Bei der Main wird "package application;" angegeben, habe ich leider nur vergessen in den Code einzufügen, das war mein Fehler...

Anhang anzeigen 16379
Noch ein Nachtrag zum Namespace:

Java:
xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"

Das ist bei allen drei fxml-Dateien identisch.
 
K

kneitzel

Gast
Ach, wollte gerade schreiben, dass es doch alles richtig aussieht aber vor dem Posten habe ich noch einmal genau geschaut. Ist aber auch leicht zu übersehen:
FXMLLoader.load(getClass().getResource("Scene1.fxml")); (aus Main)
FXMLLoader.load(getClass().getClassLoader().getResource("Scene3.fxml")); (aus dem Controller)

Schmeiss das getClassLoader() bitte raus.

Der Unterschied ist: Bei dem getResource auf der Klasse stehst Du im "Ordner", der dem namespace der Klasse entspricht.
Bei dem getResource des ClassLoaders stehst Du im Root.

Das erste wäre also ein "/application/Scene1.fxml" und das zweite ein "/Scene2.fxml". (Wenn man es absolut und nicht relativ angibt!)
 

DaveDarell

Mitglied
Ach, wollte gerade schreiben, dass es doch alles richtig aussieht aber vor dem Posten habe ich noch einmal genau geschaut. Ist aber auch leicht zu übersehen:
FXMLLoader.load(getClass().getResource("Scene1.fxml")); (aus Main)
FXMLLoader.load(getClass().getClassLoader().getResource("Scene3.fxml")); (aus dem Controller)

Schmeiss das getClassLoader() bitte raus.

Der Unterschied ist: Bei dem getResource auf der Klasse stehst Du im "Ordner", der dem namespace der Klasse entspricht.
Bei dem getResource des ClassLoaders stehst Du im Root.

Das erste wäre also ein "/application/Scene1.fxml" und das zweite ein "/Scene2.fxml". (Wenn man es absolut und nicht relativ angibt!)
Das ist es, dass ist die Lösung, tausend Dank an dich!

Nochmal zu meinem Verständnis, wenn ich dann quasi im Root stehe, muss ich noch den Pfad /application/ angeben? Falls ich das so richtig verstanden habe, würde dann beim Controller ein "/application/Scene2.fxml" klappen?
 
K

kneitzel

Gast
Ja, du kannst es generell (also egal ob auf der class oder dem clasloader aufgerufen) als absoluten Pfad (führender /) angeben.

Durchaus üblich ist aber der Weg, wie du es in der main gemacht hast. Hat den Vorteil (bei eclipse Projekten), dass die fxml schön bei ihren Klassen liegen.

Wenn man später Maven oder Gradle nutzt, dann weicht man eher davon ab, denn die Ressourcen will man nicht in so langen Pfaden haben wie de/kneitzel/MeineTolleApp/ui/hauptfenster/ sondern da nehme ich dann nur /ui/hauptfenster/. Das sind aber Dinge, die jeder so handhaben kann, wie er möchte.
 

DaveDarell

Mitglied
Ja, du kannst es generell (also egal ob auf der class oder dem clasloader aufgerufen) als absoluten Pfad (führender /) angeben.

Durchaus üblich ist aber der Weg, wie du es in der main gemacht hast. Hat den Vorteil (bei eclipse Projekten), dass die fxml schön bei ihren Klassen liegen.

Wenn man später Maven oder Gradle nutzt, dann weicht man eher davon ab, denn die Ressourcen will man nicht in so langen Pfaden haben wie de/kneitzel/MeineTolleApp/ui/hauptfenster/ sondern da nehme ich dann nur /ui/hauptfenster/. Das sind aber Dinge, die jeder so handhaben kann, wie er möchte.
Ah perfekt, dann habe ich wieder was neues gelernt. Nochmal vielen Dank und schon mal ein schönes Wochenende! :)
 

DaveDarell

Mitglied
Guten Abend,

ich hätte nochmal eine Frage - wie kann ich in dem obenstehenden Szenario verschiedene ResourceBundles einbinden, damit bei bestimmten Einstellungen die Sprache geändert wird und die Labels / Buttons etc. innerhalb der Scenes aus dem ResourceBundle geladen werden?

Folgenden Code habe ich in der Main

Java:
public void start(Stage stage) throws Exception {
String locale = new String("de_DE");
FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("Scene1.fxml"));
        loader.setResources(ResourceBundle.getBundle("bundles.language_"+ locale));
        //
        
        Parent root = loader.load();

        Scene scene = new Scene(root);

        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stage.setScene(scene);
        stage.show();
    
}

und das steht bspw. im Scene2 Controller:

Java:
    public void switchBack(ActionEvent event) throws IOException {
    
       String locale = new String("de_DE");
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("Scene1.fxml"));
        loader.setResources(ResourceBundle.getBundle("bundles.language_"+ locale));
        
        //Parent tableView = FXMLLoader.load(getClass().getResource("Login.fxml"));
        Parent root = loader.load();
        Scene tableViewscene = new Scene(root);
        Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
        tableViewscene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        window.setScene(tableViewscene);
        window.show();
    }

Irgendwie bekomme ich das im Scene2Controller nicht zum laufen, meine Vermutung ist, dass es mit dem window zusammenhängt.
Hat mir jemand eine Idee bzw. ein Tipp was ich hier falsch mache?
 
K

kneitzel

Gast
Du erstellst das ResourceBundle auch falsch.

Ein ResourceBundle lädt mehrere properties Dateien. Du hast also eine default Datei und dann noch spezifische für andere Sprachen.
Und dann lädst Du das per ResourceBundle.getBundle("bundles.laguage", locale);
Und locale ist dann auch kein String sondern vom Typ Locale.

Generell ist ein new String("...") extrem dubios. Wozu erstellst Du eine neue String Instanz und willst nicht die String Instanz des Literals verwenden?

Bezüglich ResourceBundle findet sich auch viel im Netz, z.B.
 

DaveDarell

Mitglied
Also ich habe zwei Files, 1x language_de_properties und language_en_properties.
Wenn ich dich da jetzt richtig verstanden habe, dann ist hier eins davon mein default (in dem Fall de) und das en-Bundle lade ich dann über
"ResourceBundle.getBundle("bundles.laguage", locale);" rein?

Ja das mit dem String wurde uns so beigebracht, aber generell kann ich das ja auch String s = "de_DE" nennen, das meinst du doch, oder?

Muss dazu sagen, dass ich ein absoluter Neuling bin bei JavaFX und den ResourceBundles erst Recht
 
K

kneitzel

Gast
Ich hoffe du meintest jeweils mit .properties (also kein _).
Leg noch ein language.properties an. Das ist das default File. Wenn Du z.B. kein Locale angibst, dann würde er immer mit dem Locale mit dem Java gestartet wurde, es versuchen.

Sprich: Bei einer Einstellung de_de würde er erst nach einem language_de_de.properties schauen. Wenn die Datei nicht gefunden wird, dann wird nach language_de.properties geschaut. Wird diese Datei auch nicht gefunden, dann wird language.properties genommen.

Wenn dann ein key aufgelöst wird, dann wird die Suchreihenfolge auch beibehalten. Wenn also der key in language_de_de.properties nicht vorhanden ist, dann wird in den anderen Dateien auch geschaut.

(Ich muss gestehen, dass ich dies bei Java selbst so im Detail auch noch nicht ausprobiert habe. Ich habe da vor allem Erfahrungen im .Net Bereich und habe da einiges übertragen. Es kann also durchaus gewisse Abweichungen geben. Das was ich bisher aber alles gelesen habe und so wie es sich bei mir verhalten hat, müsste es so korrekt sein! Aber ich will es halt sagen - würde normalerweise selbst prüfen und dann auch Links zur Quellen schicken, aber ich bin da im Augenblick etwas zu lustlos zu, sorry)


Was den String angeht: ja genau. Das new String("...") kenne ich eigentlich nur als Demo, um zu zeigen, dass man Strings nicht mit == vergleicht. Es gibt für Strings eine interne Liste. Auf die Liste kommen z.B. alle Literale, die irgendwo auftauchen. Dadurch gibt es Literale nur einmal.
Das kannst Du auch etwas nachspielen - String.intern() wäre hier die Methode, die aufzurufen ist.

Bei der Codezeile, die Du da hast, hast Du den String zwei Mal im Speicher. Einmal wurde das Literal natürlich mit der Klasse / dem Code geladen. Und dann hast Du eine neue String Instanz erzeugt, die den gleichen Inhalt hat. ==> Doppelter Speicherverbrauch.
Jetzt kommt noch ein ganz wichtiger Punkt hinzu: Strings sind immutable - unveränderbar. Du kannst einen String nicht verändern. Daher macht es keinen Sinn, diesen doppelt zu haben.


Wenn man nun zu Deinem Code zurück geht, dann kannst Du die Zeile aber fast so lassen. Du musst dann String nur durch Locale ersetzen. Und dann beim Resource Bundle den _ wegnehmen und statt dem + ein , nutzen um die Locale als zweiten Parameter mit zu geben.
 

DaveDarell

Mitglied
Ja also das ist alles mit .properties abgespeichert, dass habe ich vergessen.
Ok und kann ich theoretisch auch das language_de als default setzten? Sprich das dieses File automatisch geladen wird und nur wenn ein Button geklickt wird, dass language_en.properties geladen wird und die Bezeichnungen von Englisch auf Deutsch geswitcht werden?
Nenne ich das dann einfach in language.properties, damit es als default erkannt wird?

Der Code würde dann wie folgt aussehen

Java:
    public void switchBack(ActionEvent event) throws IOException {
        Locale locale = new Locale("de");
        
        
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("Scene1.fxml"));
        loader.setResources(ResourceBundle.getBundle("bundles.language_", locale));

        // Parent tableView = FXMLLoader.load(getClass().getResource("Login.fxml"));
        Parent root = loader.load();
        Scene tableViewscene = new Scene(root);
        Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
        tableViewscene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        window.setScene(tableViewscene);
        window.show();
    }

Und danke für die Erklärung mit dem String, dass war mir vorher noch nicht so klar, werde ich auf jeden Fall berücksichtigen!
 
K

kneitzel

Gast
Bei dem getBundle Aufruf muss das _ Zeichen am Ende noch weg. Und wenn de der Default sein soll, dann kannst du das File einfach entsprechend umbenennen.

Und es heisst dann einfach bundles.language.properties (die Datei)
 

DaveDarell

Mitglied
Bei dem getBundle Aufruf muss das _ Zeichen am Ende noch weg. Und wenn de der Default sein soll, dann kannst du das File einfach entsprechend umbenennen.

Und es heisst dann einfach bundles.language.properties (die Datei)
das "_" am Ende kommt daher, dass die Dateien ja in dem Ordner Bundle liegen und mit "language_de.properties" abespeichert sind, deshalb brauche ich das (zumindest nach meinem Verständnis)

Nochmal zu meinem Verständnis, dann gebe ich in jedem Controller diese Locale an und lade die dann mit
"loader.setResources(ResourceBundle.getBundle("bundles.language", locale));"

Weil falls ja, dann habe ich es glaub jetzt begriffen und auf den ersten Blick scheint es auch zu funktionieren.
Schonmal vielen Dank und noch einen schönen Abend!
 
K

kneitzel

Gast
das "_" am Ende kommt daher, dass die Dateien ja in dem Ordner Bundle liegen und mit "language_de.properties" abespeichert sind, deshalb brauche ich das (zumindest nach meinem Verständnis)
Daher schreibe ich das doch immer wieder: Das ist falsch!

Du hast ein ResourceBundle - das besteht aus mehreren .properties Dateien.

Die Basis Datei hat dann irgend einen Namen wie z.B. "Name" und heißt dann Name.properties.
Dann gibt es für diverse Locale spezielle Dateien - die bekommen dann mit "_" abgetrennt, die Locale in Kleinbuchstaben angehängt. Also z.B. Name_de.properties.

Der Name des Bundles ist aber dann "Name" und wird dann auch genau so angegeben.

Konkretes Beispiel, damit Du einfach siehst, was ich meine:
Kleine Testklasse:
Java:
package example;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

public class InternationalizingExample {
    public static void main(String[] args) {
        printMessages(ResourceBundle.getBundle("example/messages", Locale.GERMAN));
        printMessages(ResourceBundle.getBundle("example/messages", Locale.ENGLISH));
        printMessages(ResourceBundle.getBundle("example/messages", new Locale("en_US")));
        printMessages(ResourceBundle.getBundle("example/messages", Locale.CHINESE));
    }

    public static void printMessages(ResourceBundle bundle) {
        System.out.println(bundle.getString("farbe"));
        System.out.println(bundle.getString("nur_en"));
        System.out.println(bundle.getString("nur_basis"));
        try {
            System.out.println(bundle.getString("gibt es nicht"));
        } catch (MissingResourceException ex) {
            System.out.println("Eintrag nicht gefunden!");
        }
    }
}

Nun habe ich ein ResourceBundle - imNamespace example mit Namen messages. Also finden sich die folgenden Dateien bei der Java Klasse:
[CODE lang="java" title="messages.properties"]farbe = Farbe
nur_en = nur in en
nur_basis = nur in Basis[/CODE]

[CODE lang="java" title="messages_en.properties"]farbe = colour
nur_en = only in en[/CODE]

[CODE lang="java" title="messages_en_us.properties"]farbe = color[/CODE]

Was siehst Du hier nun? Ich rufe für jede Lokale immer alle Keys auf sowie einen nicht existierenden.

Auswahl der verwendeten Ressource Bundle
GERMAN und CHINESE haben keine spezifische Datei -> Basis wird genommen.
ENGLISH hat eine spezifische Datei: _en wird genommen, dann die Basis
en_us hat eine spezifische Datei: _en_us wird genommen, dann _en und dann basis.

Was bedeutet dies konkret für ein paar Fälle?

Nehmen wir Farbe: Bei en_us wird dies zu color. Bei en wird dies das die BE Variante: colour

Wenn wir "nur_en" anschauen bei en_us: Das findet sich nicht in en_us, also wird in en geschaut und von dort genommen.

=> Die Suchkette wird, wie von mir schon beschrieben, abgearbeitet.
 

DaveDarell

Mitglied
Guten Morgen und vielen Dank! Anhand des Beispiels hab ich es endlich begriffen, dass ich den "_" zum abspeichern brauche, aber nicht zum eigentlichen Aufruf der Datei.

Nochmals Danke, du hast mir echt weiter geholfen hier! :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
perlenfischer1984 JavaFX : JTreeView mit verschieden Objekten AWT, Swing, JavaFX & SWT 0
W Java Popup bei Win7 und Mac verschieden? AWT, Swing, JavaFX & SWT 2
D verschieden große Componenten in einem JPanel? AWT, Swing, JavaFX & SWT 2
M SWT Darstellung Linux / Win verschieden? AWT, Swing, JavaFX & SWT 4
G Double-Click f. alle Komps UNIX u. WIN verschieden ? AWT, Swing, JavaFX & SWT 5
B JavaFX Bei Scenen-Wechsel im primaryStage wird aktuelle Fenstergröße nicht mit übernommen AWT, Swing, JavaFX & SWT 16
Bluedaishi JavaFX Programm start mit zwei scenen bzw Fenster AWT, Swing, JavaFX & SWT 1
T Elemente auf vorheriger Stage, nach Wechsel der Stage ansprechen AWT, Swing, JavaFX & SWT 32
Augenblau JavaFX Externe Schriftart über CSS ansprechen und zuweisen AWT, Swing, JavaFX & SWT 5
N JavaFX GUI Elemente einer anderen (FXML)Klasse ansprechen AWT, Swing, JavaFX & SWT 16
J Swing/AWT | Dynamisch erzeugte Objekte ansprechen AWT, Swing, JavaFX & SWT 1
M SWT Wie Objektinstanzen ansprechen? AWT, Swing, JavaFX & SWT 10
F JTabbedPane bestimmten Tab ansprechen AWT, Swing, JavaFX & SWT 13
X Fenster von außen ansprechen AWT, Swing, JavaFX & SWT 7
R JavaFX TableView - nicht ausgewählte Reihe ansprechen AWT, Swing, JavaFX & SWT 6
D OpenGL ansprechen AWT, Swing, JavaFX & SWT 2
B Swing jnect: Kinect ansprechen AWT, Swing, JavaFX & SWT 5
J Anonyme Klasse - Button ansprechen - Warum muss Button final sein? AWT, Swing, JavaFX & SWT 4
D Bestimmtes JPanel innerhalb eines JTabbedPane ansprechen AWT, Swing, JavaFX & SWT 2
D Swing JList: ScrollPane auf JPanel ansprechen AWT, Swing, JavaFX & SWT 7
B LayoutManager GUI-Design: eventuelle Alternativen zum Verschachteln + Ansprechen von JPanel()? AWT, Swing, JavaFX & SWT 2
N OK-Button in JOptionPane ansprechen AWT, Swing, JavaFX & SWT 5
A Button in Tabelle ansprechen AWT, Swing, JavaFX & SWT 6
J Ansprechen untergeordneter Objekte AWT, Swing, JavaFX & SWT 6
N paintComponent für Graphics ansprechen AWT, Swing, JavaFX & SWT 2
F checkboxen auslesen/ansprechen AWT, Swing, JavaFX & SWT 1
M Objekt im ActionListener ansprechen AWT, Swing, JavaFX & SWT 3
S buttons über getSelected ansprechen AWT, Swing, JavaFX & SWT 4
A Drag'n Drop: Absender direkt ansprechen? AWT, Swing, JavaFX & SWT 2
G Bildpixel ansprechen und Farbwerte abfragen AWT, Swing, JavaFX & SWT 4
J JSplitPane Felder ansprechen AWT, Swing, JavaFX & SWT 3
F Checkboxen eines anderen Panels ansprechen AWT, Swing, JavaFX & SWT 3
L Drucker ansprechen unter Java AWT, Swing, JavaFX & SWT 6
S Swing - JFrame ansprechen AWT, Swing, JavaFX & SWT 2
M jLabel per Index ansprechen? AWT, Swing, JavaFX & SWT 8
G Unterschiedliche Textfields ansprechen AWT, Swing, JavaFX & SWT 4

Ähnliche Java Themen

Neue Themen


Oben