Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Java

Really weird Java problem goes against everything I've understood so far

I hope this isn't too confusing, the question looks so big now I've posted it! Code below.

I'm building a JavaFX program. I want to be able to change scenes within a single stage upon a button clicking event.

To accomplish this I am getting the stage and scenes constructed in Main.java from a method in Controller.java so that they are accessible for my action handling event. Maybe this isn't best practise but it seems logical and I know no different.

In the Controller.java file, I have a Stage variable to store my Stage and a HashMap variable to store my scenes. This is because I wanted each scene to have a string object associated with it, so it would be easier to call in a method later, i.e. (for each map entry scene in Map of scenes, if getValue is "scene3", set nextScene's value to getKey).

What is happening, is when 'start' is run at launch, it calls the get method in Controller.java, which stores the stage and scenes in their respective Stage and HashMap variables of the Controller.java class. I have a debugging bit of code that prints the values of the variables in the Controller.java, and it shows that the variables in Controller.java now contain my stage and my scenes. So far so good...

Immediately afterwards, if I run the SAME debug code from the event handler by clicking my debug button on screen, the same variables are returned as empty.

How can this be?

Main.java

package sample;

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

public class Main extends Application {


    @Override
    public void start(Stage primaryStage) throws Exception{
        Controller controller = new Controller();
        Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
        Parent firstMenu = FXMLLoader.load(getClass().getResource("/fxml/firstMenu.fxml"));
        Scene mainScene = new Scene(firstMenu,800,600);



        primaryStage.setTitle("HostelMaster");
        primaryStage.setScene(new Scene(root, 800, 600));

        controller.importStage(primaryStage);
        controller.importScene(mainScene,"Main Scene");

        primaryStage.show();



    }

    public static void main(String[] args) {

        launch(args);


    }





}

Controller.java

package sample;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import sample.model.Session;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//import java.awt.*;

public class Controller {

    private Session session;
    private Map<Scene,String> scenes;
    private Stage mainStage;

    @FXML private TextField username;
    @FXML private PasswordField password;

    public Controller(){
        session = new Session();
        scenes = new HashMap<>();
    }

    public void importStage(Stage stage){
        mainStage = stage;
        System.out.printf("imported stage: " + mainStage.toString() + "%n");

    }

    public void importScene(Scene scene, String description){
        scenes.put(scene, description);
        System.out.printf("imported scene: " + scene.toString() + "%n");
        System.out.printf("imported scenes in map of scenes: %n");
        for (Map.Entry importedscene : scenes.entrySet()){
            System.out.printf(importedscene.getKey().toString() + "%n");
            System.out.printf(importedscene.getValue().toString() + "%n");
        }

        System.out.printf("Calling debugScenes method currently returns: ");
        debugScenes();
        System.out.printf("So variables are now stored correctly as hoped.");
    }

    private void debugScenes() {
        System.out.printf("Contents of 'scenes' in Controller are as follows: %n");

        System.out.printf(scenes.toString() + "%n");
        System.out.printf(scenes.keySet().toString() + "%n");

        for(Map.Entry scene : scenes.entrySet()){
            System.out.printf("Scene: " + scene.getKey().toString() + "%n");
            System.out.printf("Desc: " + scene.getValue().toString() + "%n");
        }
    }

    public void changeScene(Stage stage, Scene scene){
        stage.setScene(scene);
    }


    public void checkLogInBtn(ActionEvent actionEvent){
        System.out.println("Log in button was clicked");

        session.populateUserlist();

        List<String> validUsernames = session.getValidUsernames();
        List<String> validPasswords = session.getValidPasswords();



        for(String validUser : validUsernames){
            System.out.printf("Username: " + validUser + "%n");
        }

        for(String validPassword : validPasswords){
            System.out.printf("Password: " + validPassword + "%n");
        }

        if(isValidEntry(username.getText(),validUsernames) && isValidEntry(password.getText(),validPasswords)){
            System.out.println("Successful login");
        }else{
            System.out.println("Invalid details");
        }


    }

public void checkNewUserBtn(ActionEvent actionEvent){
        System.out.println("New User button was clicked");

        Scene nextScene=null;
        for(Map.Entry scene : scenes.entrySet()){
            if(scene.getValue() == "Main Scene"){
                nextScene = (Scene) scene.getKey();
                System.out.printf("Scene recognised: " + nextScene.toString() + "%n");
                System.out.println("Correct scene found.");
            }
        }

        if(nextScene!=null){
            changeScene(mainStage, nextScene);
            System.out.println("Scene should have changed");
        }



    }

    private Boolean isValidEntry(String entry, List<String> validEntries){

        Boolean isValid = false;

        for(String validEntry : validEntries) {
            if (entry.equals(validEntry)) {
                isValid = true;
            }
        }
        return isValid;
    }


    public void checkDebugScenesBtn(ActionEvent actionEvent) {
        System.out.printf("Same method returned with the button event: %n");
        debugScenes();
        System.out.printf("So where have the values gone?");



    }
}

And this is what the console reads out if I just run the program and then hit my debug button, running my scene debug code again.

imported stage: javafx.stage.Stage@724dbfc3
imported scene: javafx.scene.Scene@246a2658
imported scenes in map of scenes: 
javafx.scene.Scene@246a2658
Main Scene
Calling debug
Scenes method currently returns: Contents of 'scenes' in Controller are as follows: 
{javafx.scene.Scene@246a2658=Main Scene}
[javafx.scene.Scene@246a2658]
Scene: javafx.scene.Scene@246a2658
Desc: Main Scene
So variables are now stored correctly as hoped.Same method returned with the button event: 
Contents of 'scenes' in Controller are as follows: 
{}
[]
So where have the values gone?
Process finished with exit code 0

1 Answer

Craig Dennis
STAFF
Craig Dennis
Treehouse Teacher

Hey Cameron!

Check out the FXMLLoader.getController method to get a proper handle on the controllers. The FXML creates that for you, so my guess is the wiring is wrong somewhere in there. Are they both pointing to the same controller? There should be a separate instance for each FXML file you load.

That make sense?

I think I understand. So, in the Main.java file, I had initialized a Controller in the 'normal' way.

Controller controller = new Controller();

when I SHOULD have used FXMLLoader.getController which I guess would be:

Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
Controller controller = root.getController();

So I guess you could think of it as though getController() gets the controller from the FXML file it's called on. Is that right?