How to create multiple javafx controllers with different fxml files? - java

I've been looking at some blogs and other stackoverflow questions, and I'm not seeing a direct answer to my question. I am creating a javafx gui client and I want to have my menubar be one controller in one fxml and then i want the content area to be additional fxml files. The login screen will be one fxml, after the login screen will be the main content of the application and that will be in one fxml. How do i go about doing this?
I just don't want to have all of my code for my login, menubar, and main content in the same file. This is an image of what i am working on:

Use FXML as components by using a custom java class as fx:root and as fx:controller of your FXML file: http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm
To do so, you need to call in the constructor of your custom java class FXMLLoader which will load your FXML.
The advantage is to change the way FXML load components.
The classic way to instanciate components via FXMLLoader with nested controllers is: FXML first, then controller for each part.
With this technique this is: controller first, then FXML for each component. And you won't load FXML in FXML directly, you will import your custom java classes in the FXML.
This is a better abstraction (no need to know how a component is implemented when you import them in FXML) and helps reusing code as it is like implementing a custom widget with FXML support. To make your component reusable, make sure your implementation doesn't have tight coupling with other parts, or use IOC to do so (for instance, with Spring integration with JavaFX). This way, you will be able to import your component in any part of your application (just like a DateInput widget) without worry and you won't duplicate code.
In your case you will have:
public class MenuBox extends VBox {
#FXML
private LoginBox loginBox;
#FXML
private ProfilesBox profilesBox;
public MenuBox() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("menu.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public class LoginBox extends VBox {
public LoginBox() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("login.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public class ProfilesBox extends VBox {
public ProfilesBox() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("profiles.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
And you will import LoginBox and ProfilesBox in menu.fxml that manages the global layout for your page:
<?import com.foo.bar.LoginBox ?>
<?import com.foo.bar.ProfilesBox ?>
<fx:root type="javafx.scene.layout.VBox"
xmlns:fx="http://javafx.com/fxml">
<!-- Stuff here to declare the menu bar-->
<HBox>
<ProfilesBox fx:id="profilesBox"/>
<LoginBox fx:id="loginBox"/>
</HBox>
</fx:root>
login.fxml and profiles.fxml contain just basic components.

You can include FXML documents one within the other - this should help you with separating the design logic
This means you can have Nested Controllers - one for each document.
From the documentation, you can now setup your code such that logic can be separated as well as invoked from the root controller (if need be).
Hope that helps.

I needed a popup window with similar requirements (more control over the nodes and the layout).
Having worked my way through the recommendations, I found a solution that might be useful.
First, I created a second fxml document and second controller (in NetBeans, New -> Empty FXML ... -> Use Java Controller -> Create New ...).
A little challenging was to figure out how to build the stage in the main controller and connect it to the popup controller.
The link Passing Parameters JavaFX FXML gives some real good insights and techniques.
The final code looks like this (I hope it can help someone):
// Anchor Pane from the popup
#FXML
AnchorPane anchorPanePopup;
#FXML
private void soneButtonAction(ActionEvent event) throws IOException {
Stage newStage = new Stage();
AnchorPane anchorPanePopup = (AnchorPane) FXMLLoader.load(getClass().getResource("Popup_FXML.fxml"));
Scene scene = new Scene(anchorPanePopup);
newStage.setScene(scene);
newStage.initModality(Modality.APPLICATION_MODAL);
newStage.setTitle("Dialog Window");
newStage.showAndWait();
}

package javafxapplication11;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
public class FXMLDocumentController implements Initializable {
#FXML
private CheckBox c1;
#FXML
private CheckBox c2;
public void clicked1(ActionEvent e) throws IOException {
Parent home_page_parent
=FXMLLoader.load(getClass().getResource("AddDcuFXML.fxml"));
Scene home_page_scene = new Scene(home_page_parent);
Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
app_stage.hide(); //optional
app_stage.setScene(home_page_scene);
app_stage.show();
}
public void clicked2(ActionEvent e) throws IOException {
Parent home_page_parent
=FXMLLoader.load(getClass().getResource("ViewDCU.fxml"));
Scene home_page_scene = new Scene(home_page_parent);
Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
app_stage.hide(); //optional
app_stage.setScene(home_page_scene);
app_stage.show();
}
public void clicked3(ActionEvent e) throws IOException {
Parent home_page_parent
=FXMLLoader.load(getClass().getResource("ViewDCU.fxml"));
Scene home_page_scene = new Scene(home_page_parent);
Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
app_stage.hide(); //optional
app_stage.setScene(home_page_scene);
app_stage.show();
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
}

Related

Loading a FXML Controller at Runtime from a JAR file

I am working on coding a modular soundboard in Java and JavaFX using FXML. One of key aspects is the ability to load an arbitrary class that implements the Plugin abstract class at runtime.
The plugin abstract class looks like this:
import javafx.scene.control.Tab;
import java.io.File;
import java.io.FileReader;
import java.net.URLClassLoader;
import java.util.Properties;
public abstract class Plugin {
protected Tab mainTab;
protected Tab settingsTab;
protected Properties propertyFile = new Properties();
protected File fileName;
protected URLClassLoader loader;
public Plugin(){
mainTab = new Tab();
}
public Plugin(String tabName){
mainTab = new Tab(tabName);
settingsTab = new Tab(tabName);
}
public abstract void save();
public Tab getMainTab(){
return mainTab;
}
public Tab getSettingsTab(){
return settingsTab;
}
public void initPropertyFile(String filename){
try{
File file = new File(System.getenv("APPDATA")+"\\Testing\\"+filename+".properties");
if(file.exists())
propertyFile.load(new FileReader(file));
else{
file.createNewFile();
propertyFile.load(new FileReader(file));
}
}catch(Exception e){
e.printStackTrace();
}
}
public void setClassLoader(URLClassLoader loader){
this.loader = loader;
}
public abstract void initController();
public Properties getPropertyFile(){
return propertyFile;
}
}
I use a URLClassLoader to load any classes found in a specified JAR file, and then I instantiate any instances of Plugin and collect them inside a list. Where I am having an issue is creating an FXML controller to use for these classes that I instantiate at runtime. Because both these classes and the controllers are loaded at runtime using a custom URLClassLoader I am unable to just specify a FXML controller inside my FXML files. If I try to, I get an error saying that the class I am using for a controller could not be found. If I don't specify a controller inside the FXML file, it is able to load the UI components, but I can't specify any actions inside it. I can set a controller using the FXMLLoader.setController() method, but when I load a controller like this though, none of the buttons I am using have any actions. I've tried using the initialize() method with FXML annotations, but this method isn't automatically called by the FXMLLoader. If I call initialize myself then it seems like the FXML hasn't been injected yet, but the UI components specified by the FXML file appear with the formatting specified by the FXML file on my screen.
import java.net.URL;
public class Plugin3 extends Plugin {
FXMLLoader mainTabLoader;
URL testURL;
public Plugin3(){
//Calls Plugin Constructor to initialize mainTab and settingsTab, and set the UI names of the tabs
super("Plugin3");
//Try creating FXMLLoader to setup the UI of the plugin
try {
mainTabLoader = new FXMLLoader();
//mainTabLoader.setController(this);
Plugin3 thisObj = mainTabLoader.getController();
testURL = getClass().getResource("testFXML.fxml");
//Load the FXML file
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void save() {
}
//Attempts to set the controller of the FXMLLoader mainTabLoader and call the initialize method of that controller
#Override
public void initController() {
try {
String classPath = "com.example.plugindevelopment.Plugin3Controller";
mainTabLoader.setController(Class.forName(classPath, true, loader).getConstructor().newInstance());
getMainTab().setContent(mainTabLoader.load(testURL));
mainTabLoader.getController().getClass().getDeclaredMethod("initialize").invoke(mainTabLoader.getController());
}catch(Exception e){
e.printStackTrace();
}
}
}
This is the controller:
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
public class Plugin3Controller
{
#FXML
VBox root;
#FXML
Label label;
#FXML
Slider slider;
#FXML
Button button1;
protected void onButtonClick(){
System.out.println("Hello");
}
#FXML
public void initialize(){
System.out.println("Initializing Plugin3Controller");
button1 = new Button("Hello");
button1.setOnAction(event -> onButtonClick());
}
}
and this is the FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<children>
<VBox fx:id="root" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="label" prefHeight="137.2" text="text" />
<Slider fx:id="slider">
<VBox.margin>
<Insets top="100.0" />
</VBox.margin>
<padding>
<Insets top="50.0" />
</padding></Slider>
<Button fx:id="button1" mnemonicParsing="false" text="Click Me" />
</children></VBox>
</children>
</AnchorPane>
I would appreciate any insight anyone can provide, thank you.
Because both these classes and the controllers are loaded at runtime using a custom URLClassLoader I am unable to just specify a FXML controller inside my FXML files. If I try to, I get an error saying that the class I am using for a controller could not be found.
Note that you can set the Class Loader to be used by the FXMLLoader.
The solution you attempt in which you set the controller manually has a number of errors. Most importantly:
String classPath = "com.example.plugindevelopment.Plugin3Controller";
mainTabLoader.setController(Class.forName(classPath, true, loader).getConstructor().newInstance());
getMainTab().setContent(mainTabLoader.load(testURL));
Here mainTabLoader.load(testURL) invokes the static method FXMLLoader.load(URL). Since this is a static method, it doesn't reference the FXMLLoader instance mainTabLoader at all, and in particular won't be aware of the controller instance.
You should use
String classPath = "com.example.plugindevelopment.Plugin3Controller";
mainTabLoader.setController(Class.forName(classPath, true, loader).getConstructor().newInstance());
mainTabLoader.setLocation(testURL);
getMainTab().setContent(mainTabLoader.load());
Note the call to load() has no arguments, invoking the instance method FXMLLoader.load().
Once the FXMLLoader is properly referencing a controller instance, it will invoke initialize() automatically. You should remove the line
mainTabLoader.getController().getClass().getDeclaredMethod("initialize").invoke(mainTabLoader.getController());
You also have an error in your controller implementation (I'm guessing this may have been a desperate attempt to fix the problems caused by the error above).
It is always a mistake to instantiate objects and assign them to #FXML-annotated fields, as you do with
button1 = new Button("Hello");
Remove this line. It causes the event handler to be set on a button that is not displayed in the UI, instead of on the button that is declared in the FXML file.
In all you should have
public class Plugin3Controller
{
#FXML
VBox root;
#FXML
Label label;
#FXML
Slider slider;
#FXML
Button button1;
protected void onButtonClick(){
System.out.println("Hello");
}
#FXML
public void initialize(){
System.out.println("Initializing Plugin3Controller");
button1.setOnAction(event -> onButtonClick());
}
}
and
public class Plugin3 extends Plugin {
FXMLLoader mainTabLoader;
URL testURL;
public Plugin3(){
//Calls Plugin Constructor to initialize mainTab and settingsTab, and set the UI names of the tabs
super("Plugin3");
//Try creating FXMLLoader to setup the UI of the plugin
mainTabLoader = new FXMLLoader();
//mainTabLoader.setController(this);
// This line is patently nonsense: remove it:
// Plugin3 thisObj = mainTabLoader.getController();
testURL = getClass().getResource("testFXML.fxml");
//Load the FXML file
}
#Override
public void save() {
}
//Attempts to set the controller of the FXMLLoader mainTabLoader and call the initialize method of that controller
#Override
public void initController() {
try {
String classPath = "com.example.plugindevelopment.Plugin3Controller";
mainTabLoader.setController(Class.forName(classPath, true, loader).getConstructor().newInstance());
mailTabLoader.setLocation(testURL);
getMainTab().setContent(mainTabLoader.load());
}catch(Exception e){
e.printStackTrace();
}
}
}

JavaFX #FXML annotation doesn't work when FXMLLoader is in different class than Controller? [duplicate]

This question already has answers here:
How to reference javafx fxml files in resource folder?
(3 answers)
How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?
(1 answer)
Javafx - Can application class be the controller class
(2 answers)
Closed 1 year ago.
Let's say I have three files:
Controller.java
Creator.java
Scene.fxml
Scene.fxml's controller is set to Controller.java. Controller.java calls a method from Creator.java which creates a new scene using FXMLLoader.load( ) method, then passes this scene to newly created stage and returns this stage. Controller.java calls .show() method on that returned stage. Everything is great so far, the window did pop up, but the issue is that I can not access any node from Scene.fxml in Controller.java. I naturally have "fx:id" stuff in Scene.fxml and I've also created all those nodes in Controller.java with the exact name and #FXML annotation. Anyway, they are all set to NULL.
I assume that the issue might be connected with FXMLLoader.load() method being called from s class that is not the class controller. So the question is: am I right? i
If I am, is there any way to actually make it work the way I want?
If the explanation is not good enough I will create a minimal reproducible example.
EDIT:
Controller.java:
package Issue;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class Controller extends Application {
#FXML
private Label label;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
stage = Creator.createStage(stage);
stage.show();
System.out.println("Is label null?");
if(label == null){
System.out.println("yes");
} else {
System.out.println("no");
}
}
}
Creator.java:
package Issue;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Creator {
public static Stage createStage(Stage stage) {
Parent root = null;
String pathToProject = System.getProperty("user.dir");
Path path = Paths.get(pathToProject, "src", "main", "java", "Issue", "Scene.fxml");
try{
URL url = new URL("file", "", path.toString());
root = FXMLLoader.load(url);
} catch (IOException | NullPointerException e) {
System.out.println("wrong url to fxml");
}
stage.setScene(new Scene(root, 400, 400));
return stage;
}
}
Scene.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="Issue.Controller"
prefHeight="400.0" prefWidth="600.0">
<center>
<Label fx:id="label" text="hey stackoverflow"/>
</center>
</BorderPane>
module-info.java:
module Main {
requires javafx.controls;
requires javafx.fxml;
opens Issue to javafx.fxml;
exports Issue;
}
I've created standard Maven projects and created "Issue" package with those files in src/main/java path. module-info.java is in src/main/java.

Custom FXML component (w/ controller) doesn't appear in SceneBuilder's "Import jar" dialog

I am following this in creating a custom component and importing it. However, when it gets to the Import Dialog after clicking the jar file, it does not appear. When I comment out the code the tutorial used for the constructor, it appears again. However, none of the subcomponents I used to make the custom one appear. Why and how do I fix it?
Also, I am using a VBox instead of an AnchorPane (as seen in the tutorial) for my own thing.
Code of constructor as seen in tutorial:
public CommodityImageLabel() {
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("/fxml/CommodityImageLabel.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
Code for my own example's constructor:
public While() {
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("BlocksFXML/While.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
Edit1: I tried commenting out parts of the code, and when I comment out the try-catch part, it makes the component appear in the dialog, but it still doesn't show the subcomponents.
Edit 2: The custom component is basically a VBox containing an Hbox with a Label and a TextField. Here is what it's supposed to look like vs what ends up looking like when it successfully imports without the try-catch part.
I solved this using the information here:
http://www.cuchazinteractive.com/blog/custom-javafx-controls-and-scene-builder
To summarise:
Create an FXML file and a Java class that extends the root node (I gave them the same name)
Change the FXML file to have fx:root as the base node. (will not work without this)
Remove the fx:controller attribute (will not work without this)
Compile and add the jar to scene builder
However, I have found that if your custom control depends on controls from other libraries, it will fail even if the other library is loaded in scene builder.
Below is a minimum working example
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<fx:root id="AnchorPane" prefHeight="73.0" prefWidth="112.0" type="AnchorPane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111">
<children>
<Button layoutX="25.0" layoutY="28.0" mnemonicParsing="false" text="Button" />
</children>
</fx:root>
Java:
package my.amazing.controls;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.AnchorPane;
public class TestControl extends AnchorPane {
public TestControl() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("TestControl.fxml"));
loader.setRoot(this);
loader.setController(this);
loader.load();
}
#FXML
public void initialize() {
}
}

How to close pop up windows in javafx?

I have two scenes - login prompt and Main screen. The openLoginPrompt() function opens the login prompt. Then I call the handleLogin() function which tries to authenticate a user. When login is successful I would like to close the login prompt and return to the Main screen. However if I don't set primaryStage as static primaryStage seems to be null when the handleLogin() function is called. Why does this happen and is there a better alternative to close the stage?
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package chatapplication;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
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.layout.AnchorPane;
import javafx.stage.Stage;
/**
*
* #author babaji
*/
public class Controller {
private static Stage primaryStage;
#FXML
private Button openLoginPrompt;
#FXML
private Button login;
#FXML
private TextField username;
#FXML
private PasswordField password;
#FXML
private Label loginMessage;
#FXML
private AnchorPane loginPrompt;
#FXML
private void openLoginPrompt() {
try {
primaryStage = new Stage();
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ChatApplication.class.getResource("LoginPrompt.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
#FXML
private void handleLogin() {
Database dbObj = new Database();
Connection conn = dbObj.connectToDb("chat");
String uname = username.getText();
String psswd = password.getText();
ResultSet rs = null;
try {
String sql = "SELECT * FROM user WHERE user_name=? AND password=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, uname);
ps.setString(2, psswd);
rs = ps.executeQuery();
}
catch(SQLException e) {
e.printStackTrace();
}
int count = 0;
try {
if(rs != null) while(rs.next()) count++;
}
catch(SQLException e) {
count = 0;
}
if(count == 1) {
System.out.println("Successfully Logged in");
/*This is where the problem Lies. If I dont't set primaryStage as
static, primaryStage returns null. Why does this happen and is there
some other way to close the window?s
*/
if(primaryStage != null) primaryStage.close();
}
else {
System.out.println("Failed to login.");
loginMessage.setText("Incorrect username or password");
}
}
}
You have two controller instances, one for LoginPrompt.fxml and one for another FXML you haven't shown (let's call it the "main" fxml). openLoginPrompt is called on the "main" instance, and initializes primaryStage, and loads LoginPrompt.fxml. When you load the FXML, the FXMLLoader creates a new instance of the controller, which doesn't have primaryStage initialized (assuming it's not static); so when handleLogin() is invoked on the LoginPrompt.fxml controller, it is null.
It's a really bad idea to use the same controller class for two different FXML files, as it leads to all sorts of confusing scenarios such as this, where different fields are initialized in different instances of the class. You should use a different controller class for each FXML file.
In this case, you could define a LoginController class. Edit LoginPrompt.fxml to use this controller class instead. You can expose various properties from that class and observe them from the main controller when you open the login window:
public class LoginController {
#FXML
private Label loginMessage ;
private final BooleanProperty loggedIn = new SimpleBooleanProperty();
public BooleanProperty loggedInProperty() {
return loggedIn ;
}
public final boolean isLoggedIn() {
return loggedInProperty().get();
}
public final void setLoggedIn(boolean loggedIn) {
loggedInProperty().set(loggedIn);
}
#FXML
private void handleLogin() {
// existing code you had in the previous version...
if (count == 1) {
System.out.println("Successfully logged in");
setLoggedIn(true);
} else {
System.out.println("Failed to login.");
loginMessage.setText("Incorrect username or password");
}
}
}
And then in your main controller you do
#FXML
private void openLoginPrompt() {
try {
Stage loginStage = new Stage();
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ChatApplication.class.getResource("LoginPrompt.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
LoginController loginController = loader.getController();
loginController.loggedInProperty().addListener((obs, wasLoggedIn, isNowLoggedIn) -> {
if (isNowLoggedIn) {
loginStage.hide();
}
});
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
loginStage.setScene(scene);
loginStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
There is no need to make the Stage a field with this version.
I should also mention there's a "quick and dirty" way to close the stage directly from the LoginController:
loginMessage.getScene().getWindow().hide();
which would avoid needing the property and even getting a reference to the LoginController from the main controller. However, you often have situations where you may need data from one controller in another, and the first technique is a more generally useful approach.

How to keep Java FXML active while file is loading

I am using JAVA FXML GUI to parse a text file. The user can select which file to parse and then press "load". When the user clicks on "load" the file gets parse and many lines gets added to an ArrayList. The user then search and manipulate the text that was parse that is store in the ArrayList.
Sometimes while loading/writing the file to the ArrayList or manipulating some lines, the GUI is not responsive. How can I separate the GUI process vs. the ArrayList handling in order for the GUI to always be accessible to the user ?
The code below launches my application:
package javafxapp;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Javafxapp extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
FXMLDocumentController controller = new FXMLDocumentController();
controller.openingSound();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
This is a textbook case for the use of multithreading.
See this JavaFX documentation for information and tutorials on how to begin using it.

Categories

Resources