I'm creating a dashboard for a management system using JavaFX. Also, i'm using an MVC pattern.
I'm having troubles dealing with dependency injection: my main problem was that my HomepageView contains an HomepageController variable, and on the other side HomepageController contains an HomepageView.
So, i tried with DI using constructors, but it would be cyclical so i gave up and tried DI using Setters. So, i have a Main class, and in the start method i instantiate the 2 classes using the default constructor and set each others variables. This is my main code:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HomepageView.class.getResource("/homepage/homepage.fxml"));
Scene scene = new Scene(fxmlLoader.load());
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
HomepageController homeController = new HomepageController();
HomepageView homeView = new HomepageView();
PersonalizzaMenuController personalizzaController = new PersonalizzaMenuController();
PersonalizzaMenuView personalizzaView = new PersonalizzaMenuView();
personalizzaController.setPersonalizzaMenu(personalizzaView);
personalizzaView.setPersonalizzaMenuController(personalizzaController);
homeView.setHomepageController(homeController);
homeController.setHomepageControllers(homeView, personalizzaView);
}
}
This is my Homepage view class:
package com.example.ratatouille23.homepage;
import com.example.ratatouille23.Model.Utente;
import com.example.ratatouille23.View;
import com.example.ratatouille23.View2;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class HomepageView {
@FXML
BorderPane borderPane;
@FXML
Label labelUsername;
private Stage stage;
private Scene scene;
private Parent root;
private String nomeNodo = "HomePage";
private HomepageController homepageController;
// *********************
public HomepageView(){};
@FXML
public void initialize(){
labelUsername.setText(Utente.getUsername());
}
//************************
// Action event
public void clickPulsantePersonalizzaMenu(){
homepageController.onPersonalizzaMenuClicked();
}
@Override
public Node loadNode() throws IOException {
return FXMLLoader.load(getClass().getResource("/homepage/homepage.fxml"));
}
public void setHomepageController(HomepageController homepageController) {
this.homepageController = homepageController;
}
}
And this is my HomepageController class:
package com.example.ratatouille23.homepage;
import com.example.ratatouille23.View;
import com.example.ratatouille23.creaUtente.CreaUtenteView;
import com.example.ratatouille23.inserisciAvvisi.InserisciAvvisiView;
import com.example.ratatouille23.personalizzaMenu.NuovoPiattoView;
import com.example.ratatouille23.personalizzaMenu.PersonalizzaMenuView;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
import java.util.Optional;
public class HomepageController {
HomepageView homepage;
PersonalizzaMenuView personalizzaMenu;
//**************************
// Costruttori
HomepageController(){};
//**************************
// On Action Event
public void onPersonalizzaMenuClicked() {
try {
setBorderPaneCenter(personalizzaMenu.loadNode());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//**************************
// Metodi di utility
public void setBorderPaneCenter(Node node){
homepage.borderPane.getChildren().remove(homepage.borderPane.getCenter());
homepage.borderPane.setCenter(node);
}
public void setBorderPaneRight(Node node){
homepage.borderPane.getChildren().remove(homepage.borderPane.getRight());
homepage.borderPane.setCenter(node);
}
public void setHomepageControllers(HomepageView home, PersonalizzaMenuView personalizzamenu){
this.homepage = home;
this.personalizzaMenu = personalizzamenu;
}
}
Homepage FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import java.lang.String?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>
<BorderPane fx:id="borderPane" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.ratatouille23.homepage.HomepageView">
<left>
<AnchorPane prefHeight="700.0" prefWidth="306.0" BorderPane.alignment="CENTER">
<children>
<AnchorPane layoutX="18.0" layoutY="279.0" prefHeight="421.0" prefWidth="277.0" stylesheets="@homepage.css">
<styleClass>
<String fx:value="nav-2" />
<String fx:value="shadow" />
</styleClass>
<children>
<Button layoutX="19.0" layoutY="21.0" mnemonicParsing="false" onAction="#clickPulsanteInserisciAvvisi" prefHeight="40.0" prefWidth="240.0" styleClass="nav-btn" stylesheets="@homepage.css" text="Inserisci Avvisi" />
<Button fx:id="buttonPersonalizzaMenu" layoutX="20.0" layoutY="89.0" mnemonicParsing="false" onAction="#clickPulsantePersonalizzaMenu" prefHeight="40.0" prefWidth="240.0" styleClass="nav-btn" stylesheets="@homepage.css" text="Personalizza Menu" />
<Button fx:id="buttonCreaUtente" layoutX="21.0" layoutY="158.0" mnemonicParsing="false" onAction="#clickPulsanteCreaUtente" prefHeight="40.0" prefWidth="240.0" styleClass="nav-btn" stylesheets="@homepage.css" text="Crea Utente" />
<Button layoutX="23.0" layoutY="372.0" mnemonicParsing="false" prefHeight="34.0" prefWidth="41.0" styleClass="signout" stylesheets="@homepage.css">
<graphic>
<FontAwesomeIconView glyphName="SIGN_OUT" size="2em" wrappingWidth="20.861303329467773" />
</graphic>
</Button>
<Label layoutX="75.0" layoutY="372.0" text="Log out">
<font>
<Font name="Al Nile" size="18.0" />
</font>
</Label>
</children>
</AnchorPane>
<AnchorPane layoutX="18.0" prefHeight="253.0" prefWidth="277.0" stylesheets="@homepage.css">
<children>
<FontAwesomeIconView fill="WHITE" glyphName="USER" layoutX="116.0" layoutY="63.0" size="60" />
<Label layoutX="78.0" layoutY="90.0" text="Benvenuto," textFill="WHITE">
<font>
<Font name="Tahoma" size="24.0" />
</font>
</Label>
<Label fx:id="labelUsername" layoutX="87.0" layoutY="124.0" text="UTENTE" textFill="WHITE">
<font>
<Font name="Al Nile" size="24.0" />
</font>
</Label>
</children>
<styleClass>
<String fx:value="shadow" />
<String fx:value="nav-1" />
</styleClass>
</AnchorPane>
</children>
</AnchorPane>
</left>
<top>
<AnchorPane prefHeight="41.0" prefWidth="1100.0" BorderPane.alignment="CENTER" />
</top>
<right>
<AnchorPane prefHeight="700.0" prefWidth="343.0" BorderPane.alignment="CENTER" />
</right>
<center>
<AnchorPane maxWidth="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="453.0" stylesheets="@homepage.css" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
This is PersonalizzaMenuView:
package com.example.ratatouille23.personalizzaMenu;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import java.io.IOException;
import java.util.Optional;
public class PersonalizzaMenuView {
@FXML
Accordion listaCategorie;
PersonalizzaMenuController personalizzaMenuController;
public PersonalizzaMenuView(){};
// Carica nodo schermata
public Node caricaNodoPersonalizzaMenu() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/personalizzaMenu/personalizza-menu.fxml"));
return loader.load();
}
public void setPersonalizzaMenuController(PersonalizzaMenuController personalizzaMenuController) {
this.personalizzaMenuController = personalizzaMenuController;
}
}
PersonalizzaMenuController:
package com.example.ratatouille23.personalizzaMenu;
import javafx.scene.control.Label;
import javafx.scene.control.TextInputDialog;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.AnchorPane;
import java.util.Optional;
public class PersonalizzaMenuController {
PersonalizzaMenuView personalizzaMenu;
public PersonalizzaMenuController(PersonalizzaMenuView personalizzamenu){
this.personalizzaMenu = personalizzamenu;
}
public PersonalizzaMenuController(){};
public void onPulsanteAggiungiCategoriaClicked() {
//
}
*/
}
public void setPersonalizzaMenu(PersonalizzaMenuView personalizzaMenu) {
this.personalizzaMenu = personalizzaMenu;
}
}
PersonalizzaMenu FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.scene.control.Accordion?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="682.0" prefWidth="500.0" styleClass="white-bg" stylesheets="@../homepage/homepage.css" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.ratatouille23.personalizzaMenu.PersonalizzaMenuView">
<bottom>
<HBox alignment="CENTER" prefHeight="129.0" prefWidth="500.0" spacing="60.0">
<children>
<Button mnemonicParsing="false" onAction="#clickPulsantePiu" styleClass="signout" stylesheets="@../homepage/homepage.css" text="+ Aggiungi Categoria" textFill="WHITE">
<font>
<Font name="Tahoma" size="13.0" />
</font>
</Button>
<Button mnemonicParsing="false" onAction="#clickPulsanteAggiungiPiatto" text="+ Aggiungi piatto" />
<Button mnemonicParsing="false" text="- Elimina" />
</children>
</HBox>
</bottom>
<top>
<HBox alignment="CENTER" prefHeight="130.0" prefWidth="500.0" stylesheets="@../homepage/homepage.css" BorderPane.alignment="CENTER">
<styleClass>
<String fx:value="nav-1" />
<String fx:value="shadow" />
</styleClass>
<children>
<Label alignment="TOP_CENTER" contentDisplay="TOP" text="PERSONALIZZA MENU" textAlignment="CENTER" textFill="WHITE">
<font>
<Font name="Tahoma" size="29.0" />
</font>
</Label>
</children>
</HBox>
</top>
<center>
<ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<content>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<Accordion fx:id="listaCategorie">
<panes>
<TitledPane animated="true" text="untitled">
<content>
<VBox prefHeight="118.0" prefWidth="462.0" />
</content>
</TitledPane>
</panes>
</Accordion>
</children>
</VBox>
</content>
</ScrollPane>
</center>
</BorderPane>
When i run the main method, the homepage starts, but whenever i press a button that should load another window this is the error:
mar 21, 2023 5:50:44 PM com.sun.glass.ui.mac.MacApplication lambda$waitForReactivation$6
WARNING: Timeout while waiting for app reactivation
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml@19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1857)
at javafx.fxml@19/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1724)
at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base@19/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.base@19/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics@19/javafx.scene.Node.fireEvent(Node.java:8923)
at javafx.controls@19/javafx.scene.control.Button.fire(Button.java:203)
at javafx.controls@19/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:207)
at javafx.controls@19/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at javafx.base@19/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base@19/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base@19/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics@19/javafx.scene.Scene$MouseHandler.process(Scene.java:3894)
at javafx.graphics@19/javafx.scene.Scene.processMouseEvent(Scene.java:1887)
at javafx.graphics@19/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2620)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
at javafx.graphics@19/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
at javafx.graphics@19/com.sun.glass.ui.View.notifyMouse(View.java:937)
at javafx.graphics@19/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:116)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:77)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at javafx.base@19/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml@19/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:84)
at javafx.fxml@19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1854)
... 44 more
Caused by: java.lang.NullPointerException: Cannot invoke "com.example.ratatouille23.homepage.HomepageController.onInserisciAvvisiClicked()" because "this.homepageController" is null
at com.example.ratatouille23/com.example.ratatouille23.homepage.HomepageView.clickPulsanteInserisciAvvisi(HomepageView.java:54)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
... 51 more
This sounds strange to me, because i used the setter methods, so the HomepageController should not be null.
I hope i gave all the useful informations. I'm also sure i probably not quite understood the right way to use this approach.