-2

I have tried researching various pages regarding how to switch scenes or even stages with no luck to help my specific case. I am trying to create a program with a login form that manages a school library.

I have a login form that authenticates the user input with the method validateLogin(). If the login is successful (i.e. isLoginSuccess = true), I want my program to close primaryStage and open a new stage/window with the library management system.

Is it better to switch the scene on primaryStage or close primaryStage and open a new stage? How do you do that and should you do that from Main.java or is it fine to do it from LoginController.java? I'm having trouble switching scenes or even just creating a new stage from LoginController.java since primaryStage isn't recognized in that class.

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 javafx.stage.StageStyle;

public class Main extends Application
{


    @Override
    public void start(Stage primaryStage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("login.fxml"));
        primaryStage.initStyle(StageStyle.UNDECORATED);
        primaryStage.setScene(new Scene(root, 520, 400));
        primaryStage.setResizable(false);
        primaryStage.show();
    }

    //main() method is ignored in correctly deployed JavaFX application.
    public static void main(String[] args)
    {
        launch(args);
    }


}

LoginController.java

package sample;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;

import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.stage.StageStyle;

import java.io.File;
import java.util.ResourceBundle;

//Can be removed, StackOverflow import
import java.io.BufferedReader;
import java.io.FileReader;

import java.net.URL;

public class LoginController implements Initializable
{

    @FXML
    private Button cancelButton;
    @FXML
    private Label loginMessageLabel;
    @FXML
    private ImageView brandingImageView;
    @FXML
    private ImageView lockImageView;
    @FXML
    private TextField usernameTextField;
    @FXML
    private PasswordField enterPasswordField;


    @Override
    public void initialize(URL url, ResourceBundle resourceBundle)
    {
        File brandingFile = new File("Images/LibManager.png");
        Image brandingImage = new Image(brandingFile.toURI().toString());
        brandingImageView.setImage(brandingImage);

        File lockFile = new File("Images/LoginLock.png");
        Image lockImage = new Image(lockFile.toURI().toString());
        lockImageView.setImage(lockImage);
    }

    //Activates validateLogin() if there is input in text fields.
    public void loginButtonOnAction(ActionEvent event)
    {
        if (usernameTextField.getText().isBlank() == false && enterPasswordField.getText().isBlank() == false)
        {
            validateLogin();
        }
        else
            {
            loginMessageLabel.setText("Please enter username and password");
            }
    }

    //Closes login window.
    public void cancelButtonOnAction(ActionEvent event)
    {
        Stage stage = (Stage) cancelButton.getScene().getWindow();
        stage.close();
    }

    //Compares user input to txt file contents to authenticate.
    public void validateLogin() {
        {
            try {
                String location = "userdatabase.txt";
                String username = usernameTextField.getText();
                String password = enterPasswordField.getText();

                FileReader fr = new FileReader(location);
                BufferedReader br = new BufferedReader(fr);
                String line, user, pass;
                boolean isLoginSuccess = false;
                while ((line = br.readLine()) != null) {
                    user = line.split(" ")[1].toLowerCase();
                    pass = line.split(" ")[2].toLowerCase();
                    if (user.equals(usernameTextField.getText()) && pass.equals(enterPasswordField.getText())) {
                        isLoginSuccess = true;
                        break;
                    }
                }
                if (!isLoginSuccess)
                {

                    /*
                    public void startLibManager(Stage primaryStage) {
                    FXMLLoader loader = new FXMLLoader(getClass().getResource("LibDatabase.fxml"));


                            Parent mainCallWindowFXML = loader.load();
                    secondaryStage.initStyle(StageStyle.UNDECORATED);
                    secondaryStage.setScene(new Scene(root, 520, 400));
                    secondaryStage.setResizable(false);
                    secondaryStage.show();
                    }
                     */

                    /*
                    //use one of the components on your scene to get a reference to your scene object.

                    Stage stage = (Stage)tfCallerName.getScene.getWindow();//or use any other component in your controller
                    Scene mainCallWindow = new Scene (mainCallWindowFXML, 800, 600);
                    stage.setScene(newCallDetails);
                    stage.show(); //this line may be unnecessary since you are using the same stage.
                     */
                }
                fr.close();

                } catch (Exception e) {
                e.printStackTrace();
                }
        }
    }
}

login.fxml

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="520.0" style="-fx-background-color: #FFFFFF;" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.LoginController">
   <left>
      <AnchorPane prefHeight="407.0" prefWidth="227.0" BorderPane.alignment="CENTER">
         <children>
            <ImageView fx:id="brandingImageView" fitHeight="400.0" fitWidth="226.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@../../Images/LibManager.png" />
               </image>
            </ImageView>
         </children></AnchorPane>
   </left>
   <right>
      <AnchorPane prefHeight="400.0" prefWidth="332.0" style="-fx-background-color: FFFFFF;" BorderPane.alignment="CENTER">
         <children>
            <ImageView fx:id="lockImageView" fitHeight="32.0" fitWidth="46.0" layoutX="134.0" layoutY="65.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@../../Images/LoginLock.png" />
               </image>
            </ImageView>
            <Label layoutX="23.0" layoutY="157.0" prefHeight="17.0" prefWidth="60.0" text="Username" textFill="#01989e" />
            <TextField fx:id="usernameTextField" layoutX="96.0" layoutY="152.0" prefWidth="173.0" promptText="Username" />
            <Label layoutX="26.0" layoutY="205.0" text="Password" textFill="#01989e" />
            <PasswordField fx:id="enterPasswordField" layoutX="96.0" layoutY="200.0" prefHeight="27.0" prefWidth="173.0" promptText="Password" />
            <Button fx:id="loginButton" layoutX="22.0" layoutY="294.0" mnemonicParsing="false" onAction="#loginButtonOnAction" prefHeight="27.0" prefWidth="249.0" style="-fx-background-color: ff914d;" text="Login" textFill="WHITE" />
            <Button fx:id="cancelButton" layoutX="22.0" layoutY="342.0" mnemonicParsing="false" onAction="#cancelButtonOnAction" prefHeight="27.0" prefWidth="249.0" style="-fx-background-color: ff914d;" text="Cancel" textFill="WHITE" />
            <Label fx:id="loginMessageLabel" layoutX="26.0" layoutY="248.0" prefHeight="17.0" prefWidth="160.0" textFill="RED" />
         </children></AnchorPane>
   </right>
</BorderPane>
Gaeini
  • 31
  • 5
Loritt
  • 1
  • 2
  • This **exact** scenario has been asked about and answered countless times on Stack Overflow. Which of the previous answers have you tried and how did they not work for you? – Zephyr Mar 21 '21 at 00:01
  • You can get a reference to the current window via `Node#getScene()` and `Scene#getWindow()`. – Slaw Mar 21 '21 at 00:02
  • @Zephyr, I have tried using primaryStage.close(); in LoginController.java, but it tells me that it "cannot resolve symbol". It was one of the suggestions that did not work. Is primaryStage referenced differently in other classes from Main.java? – Loritt Mar 21 '21 at 00:11
  • @Loritt You'll want to look into how Java handles "scope." Your `primaryStage` cannot be referenced from another class unless you first pass a reference to your other class. Or, you can do as @Slaw suggested and get access to the stage with the `getScene()` and `getWindow()` methods. – Zephyr Mar 21 '21 at 00:13
  • @Zephyr Does that mean that I would have to do something like this to reference primaryStage and close it in LoginController.java? primaryStage.getScene.close() – Loritt Mar 21 '21 at 00:14
  • No. That's backwards. Take a node that was injected into your controller and you can do `node.getScene().getWindow()` which will give you the window it belongs to. In your application, in this context, that window will be the primary stage. Call `close()` on it. – Slaw Mar 21 '21 at 05:59

1 Answers1

0

@Loritt I might be a bit late, but I use this method to change scenes when using JavaFX.

If you are going to be changing back and forth between scene, this method keeps you from having to write redundant code.

Stage stage;
Parent scene;
public void switchViews(ActionEvent event, String fileLocation) throws IOException {

    stage = (Stage) ((Button) event.getSource()).getScene().getWindow();
    scene = FXMLLoader.load(getClass().getResource(fileLocation));
    stage.setScene(new Scene(scene));
    stage.show();
}

I usually put this method inside the controller class which is completely acceptable as it has to do with the controlling of the visuals in the application and not any business logic.

Also, when I was originally learning how to do this myself and then pass information between the scenes I used this video and youtuber who has some good content on JavaFX/Scene Builder. (Link: https://www.youtube.com/watch?v=XCgcQTQCfJQ)

Hope this helps answer your question.

Happy coding! :)

  • Thank you for helping! Although I was able to figure it out by myself, this might be a snippet I'll use in the future. Also, the video seems very helpful to garner a deeper understanding of the topic, so thanks once again. – Loritt Mar 24 '21 at 07:23