javafx.scene.layout.Pane cannot be cast to javafx.fxml.FXMLLoader - java

I'm having trouble when trying to switch from a scene to another scene. The scenario is this:
Current View and Controller: login.fxml and LoginController
Next step View and Controller: loggedWindow.fxml and UserPanelController.
Now, I'm in LoginController and trying to switch the scene to loggedWindow.fxml sending to UserPanelController a parameter, but when I'm using my code I get:
javafx.scene.layout.Pane cannot be cast to javafx.fxml.FXMLLoader
LoginController:
FXMLLoader loggedWindow = null;
loggedWindow = FXMLLoader.load(getClass().getResource("loggedWindow.fxml")); // here crashes!
Pane root = loggedWindow.load();
UserPanelController controller = loggedWindow.getController();
controller.initData(customer);
Stage switchScene = (Stage)((Node)event.getSource()).getScene().getWindow();
switchScene.setResizable(false);
switchScene.setTitle("Welcome " + customer.FirstName + " " + customer.LastName);
switchScene.setScene(new Scene(root, 800, 500));
switchScene.show();
LoggedWindow.fxml
<Pane maxHeight="500.0" maxWidth="800.0" minHeight="500.0" minWidth="800.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Main.UserPanelController">
<children>
<ToolBar prefHeight="40.0" prefWidth="831.0">
<items>
.
.
stuff (buttons/labels and so on).
.
.
</Pane>
I would appreciate any help! Thanks in advance.
Update 1
Also took in consideration this reference: Accessing FXML controller class

You're using the "load" method of FXMLLoader which returns the root node of your .fxml file. In that case, it's returning your Pane.
You should use it to create your scene!
See the example given in the JavaFX tutorial, like:
Pane root = FXMLLoader.load(getClass().getResource("loggedWindow.fxml"));
Scene scene = new Scene(root, width, height, color);
Other way, taken from one of my old code, using non-static FXMLLoader:
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlFile));
Parent root;
try {
root = loader.load();
} catch (IOException ioe) {
// log exception
return;
}
// Color.TRANSPARENT allows use of rgba colors (alpha layer)
setScene(new Scene(root, Color.TRANSPARENT));

Related

Add a custom component in JavaFX

I would like to add a custom element into a VBox.
For example: being able to write VBox.getChildren().add(element) and element is a custom node created by me in FXML.
I already followed this tutorial: https://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm
but the example only shows how to do this inside the same controller (a single controller, i already have my "big" controller, which is WinnerController, I would like to split the two classes, one that manages the single element, and one that manages the whole scene Winner.fxml).
I already have a class WinnerController which is the controller of my FXML Winner.fxml.
Here the code of my Winner.fxml:
<AnchorPane id="paneWinner" fx:id="paneWinner" prefHeight="800.0" prefWidth="1280.0" stylesheets="#winner.css" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.gui.WinnerController">
<children>
<VBox layoutX="434.0" layoutY="125.0" prefHeight="250.0" prefWidth="413.0" spacing="10.0">
<children>
<AnchorPane id="leaderBoardElement" prefHeight="80.0" prefWidth="413.0" stylesheets="#leaderBoard.css">
<children>
<Text layoutX="49.0" layoutY="45.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" wrappingWidth="107.13671875" />
</children></AnchorPane>
</children>
</VBox>
I would like to dynamically add element with the id "leaderBoardElement" (so an AnchorPane + Text) into my VBox.
How can i do that?
Edit: I also tried with this solution:How to understand and use `<fx:root>` , in JavaFX?
but i keep getting nothing. When i do vbox.getChildren().add(new MyComponent());
called in my WinnerControlleri get nothing.
WinnerController class:
public class WinnerController implements Initializable {
#FXML
private VBox leaderBoard;
#FXML
public void initialize(URL location, ResourceBundle resources) {
System.out.println("Winner Init");
MyComponent test = new MyComponent();
System.out.println(test);
// leaderBoard.getChildren().add(new MyComponent());
}
}
My Component class:
public class MyComponent extends AnchorPane {
#FXML
private TextField textField ;
#FXML
private Button button ;
public MyComponent() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyComponent.fxml"));
loader.setController(this);
loader.setRoot(this);
loader.load();
textField.setText("HELLO!");
} catch (IOException exc) {
// handle exception
System.out.println("ELEMENT NOT CREATE!!!");
}
}
}
Winner.fxml:
<AnchorPane id="paneWinner" fx:id="paneWinner" prefHeight="800.0"
prefWidth="1280.0" stylesheets="#winner.css" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.gui.WinnerController">
<children>
<VBox fx:id="leaderBoard" layoutX="434.0" layoutY="125.0" prefHeight="250.0" prefWidth="413.0" spacing="10.0" />
MyComponent.fxml:
<fx:root type="javafx.scene.layout.AnchorPane" fx:id="leaderBoardElement" id="leaderBoardElement">
<TextField fx:id="textField" />
<Button fx:id="button" />
And i call the creation and loading of Winner.fxml from another class like this:
FXMLLoader loader = new FXMLLoader(getClass().getResource("/Winner.fxml"));
if (loader!=null)
System.out.println("LOADER NOT NULL!!");
try{
System.out.println("TRY!");
Parent root = (Parent) loader.load();
if(root!=null)
System.out.println("ROOT NOT NULL!!");
Scene startedGame = new Scene(root, 1280, 800, Color.WHITE);
if(startedGame!=null)
System.out.println("SCENE NOT NULL!");
Stage window = (Stage) paneCarta0.getScene().getWindow();
if (window!=null)
System.out.println("WINDOW NOT NULL!!");
window.setScene(startedGame);
window.show();
catch (IOException Exception) {
System.out.println("View not found. Error while loading");
}
The problem is inside the new MyComponent(), where probably i get an exception and it propagates to my main caller. I've tried everything but i can't figure out why it can't create the MyComponent object.
If your custom component is in a separate FXML file you can do this.
In your WinnerController class:
#FXML
private void initialize() throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getRessource("customElement.fxml"));
fxmlLoader.setController(new CustomElementController()); //Or just specify the Controller in the FXML file
myVBox.getChildren().add(fxmlLoader.load());
}
Edit: In this solution there should be NO <fx:include> tags in your main fxml

How to create an FXML handler that accepts parameters?

I am developing a small, fake, Mail Client in JavaFXML.
It offers a Listview with messages written on a txt file, a TextArea which prints selected message and some buttons.
Here's an Image of the Main View: https://ibb.co/iKN2rm
I already took care of "New Message" Button, which is launching a new FXML View and works well.
Here's an Image of the "New_Message" View: https://ibb.co/hQf5Bm
Now I'm trying to implement the "Reply" button, which should launch the same View as before (New Message) but set on all three TextFields strings taken from the Main View, such as Message's text, recipient and Message Argument.
Below the New Message Button Handler:
private void handle_new(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/Client/Resources/new_utente1.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("New Message");
stage.setScene(new Scene(root1));
stage.show();
} catch (Exception ecc) {
System.out.println("ERROR: " + ecc.getMessage());
}
}
I tried to implement the handle_reply method, but I'm not able to add parameters because FXML file won't find the method if I do so.
Below a small part of the FXML file:
<TextArea fx:id = "testo" editable="false" layoutX="283.0" layoutY="53.0" prefHeight="450.0" prefWidth="490.0" promptText="Select a message and it will be displayed here..." />
<Button id="nuovo" layoutX="46.0" layoutY="521.0" onAction = "#handle_new" mnemonicParsing="false" prefHeight="25.0" prefWidth="173.0" text="New Message" />
<Button id="reply" layoutX="283.0" layoutY="521.0" onAction = "#handle_reply" mnemonicParsing="false" prefHeight="25.0" prefWidth="132.0" text="Reply" />
My question is: How do I implement the "handle_reply" method as described before?
Thank you
Create a Controller class
then connect this class to your view
private void handle_new(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/Client/Resources/new_utente1.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
CController ctr = fxmlLoader.getController();
ctr.setLabelText("asdsad");
Stage stage = new Stage();
stage.setTitle("New Message");
stage.setScene(new Scene(root1));
stage.show();
} catch (Exception ecc) {
System.out.println("ERROR: " + ecc.getMessage());
}
}
and in your Controllers you'll do
public class CController implements Initializable {
#FXML TextArea testo;
#Override
public void initialize(URL location, ResourceBundle resources) {}
public void setLabelText(String text){testo.setText(text);}
}
Unfortunately it's not possible to use handlers with parameters. You should use different handler in each case.
Had this problem some time ago too. As a solution you could perform small refactoring to minimize code duplicating.
And if you know javascript you can play with this instead of using java handlers: https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html#scripting
Edit: also you can check this answer: https://stackoverflow.com/a/37902780/5572007 (pretty the same)

JavaFX BorderPane multi FXML space arrangement and transition effect

i want to design a GUI that contains BorderPane in the top of the container there is a MenuBar, in the left of the container there is a Acordion with different buttons that change the content of the center of the container for diferent FXML files, someting like the desktop app of Spotiffy
i got a working prototipe and it looks like this
the change in FXML happens and the buttos respont very well, the problem i have is that the FXML that fill the center part of the BoderPane doesn't auto rize and if is to big parts of the FXML doesn't show and if the FXML is smaller that the space left for the center part stays of the same small size and left alot of space with nothing
this is my code for the calling of the new FXML
public void lanzaUno(){
try {
// load first FXML
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Coordinador.class.getResource(
"VistaControlador/Usuario/Uno.fxml"));
/*i put the AnchorPane inside of a
ScrollPane for the desire funcionality of alot
of vertical space for many nodes in a single FXML file
*/
ScrollPane unoAnchorPane = (ScrollPane) loader.load();
UnoController controller = loader.getController();
//example method for passing variables to the FXML controler
controller.pasoPrincipal(this, primaryStage, "Rhutt");
//puts the FXML in the center of the BorderPane
rootLayout.setCenter(unoAnchorPane);
//this if for trying to accommodate the content en the BorderPane
BorderPane.setAlignment(unoAnchorPane, Pos.TOP_LEFT);
} catch (IOException e) {
e.printStackTrace();
}
}
my first cuestion is what is the thing i am missing in the calling of the FXML for the contents of this ocupy the space available in the BorderPane?
my secon cuestion regards the change of FXML, when i pass fron one to another the change in the BorderPane is in a instant and looks very bad is there a way for making the transicion like one where the content of a FXML that is call push the content of the FXML in the center?, it doesn't have to be very elaborated just make the transition a little better
EDIT
i got a cordinator class where i send and recive parameters for all the FXML and where i declare the methods of calling new FXML, so i have a cordinator class, a FXML root with its controller and two FXML and its controllers with different things in each one, this two FXML are the ones that change in the BorderPane center of the root
this is the coordinator class
//Variables
private Stage primaryStage;
private BorderPane rootLayout;
/**
* launh
* #param primaryStage
*
*/
#Override
public void start(Stage primaryStage) throws Exception{
// Inicializa la escena
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Login");
this.primaryStage.centerOnScreen();
//star method
iniLogin();
}
/**
*load the root scene
*/
public void iniLogin(){
try {
// Carga el loader.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(com.aohys.rehabSys.MVC.Coordinador.class.getResource(
"VistaControlador/Pricipal/Principal.fxml"));
rootLayout = (BorderPane) loader.load();
//the root scene
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// Da acceso al programa principal.
PrincipalController controller = loader.getController();
controller.pasoPrincipal(this, primaryStage);
primaryStage.centerOnScreen();
// Muesta la escena,
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
after this method there are two identical methods like the one at the beginning where i call the 2 changing FXML call LanzaUno, LanzaDos
this is my rood FXML controler
public class PrincipalController implements Initializable {
//variable of the coordinator class
private Coordinador cordi;
private Stage stage;
/**
* method for passing parameters to the FXML
* #param cordi
* #param stage
*/
public void pasoPrincipal(Coordinador cordi, Stage stage) {
this.cordi = cordi;
this.stage = stage;
}
//FXML in root
#FXML private Button btt1;
#FXML private Button btt2;
#FXML public static StackPane stackPane;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
//Call the firts FXML
btt1.setOnAction((evento)->{
cordi.lanzaUno();
});
//Call the second FXML
btt2.setOnAction((evento)->{
cordi.lanzaDos();
});
}
for the moment the controllers on the two FXML dont do anything
You should read up on Adam Bien's AfterburnerFX library for managing dependency injection and controllers in your application. It has changed my life. :)
For transitions, I use this code. It is a nice fade-out/fade-in from one screen to the next. In this case, stackPane is a StackPane in the center of your BorderPane defined in your FXML.
Here is a simple FXML with the aforementioned stackPane:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="borderPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.tada.gui.tada.TadaPresenter">
<center>
<StackPane fx:id="stackPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="240.0" prefWidth="320.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
And the setScreen method that changes to the new one passed in:
/**
* Set Screen
*
* #param view
* #return boolean
*/
public boolean setScreen(Parent view) {
final DoubleProperty opacity = stackPane.opacityProperty();
if (!stackPane.getChildren().isEmpty()) { //if there is more than one screen
Timeline fade = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1.0)),
new KeyFrame(new Duration(TRANSITION_TIMER), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
stackPane.getChildren().remove(0); //remove the displayed screen
stackPane.getChildren().add(0, view); //add the screen
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(TRANSITION_TIMER), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
}, new KeyValue(opacity, 0.0)));
fade.play();
} else {
stackPane.setOpacity(0.0);
stackPane.getChildren().add(view); //no one else been displayed, then just show
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(TRANSITION_TIMER), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
return true;
}
You'll also need this in your controller...
private static final double TRANSITION_TIMER = 200;
EDIT:
I tried to put together a very basic "application". It is rudimentary, but I think it does a good job of illustrating the use of AfterburnerFX and the screen fade transition. There is a lot more to AfterburnerFX. I just used the view switching without the dependency injection, which is very important when you start wanting to work on objects in your application. Also, property binding is very important for a good UX. Anyway, Here is a link to my example on GitHub.

How to obtain data from a database and put it inside fxml?

I'm making some kind of information application about a city which I need to use a database for, I got my database set up and ready and most of the code needed as well; the only part is to extract the data I need from my SQL server into a BarChart in fxml. Can anyone give me a example or something?
My fxml:
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controllers.ControllerZW">
<BarChart fx:id="graph" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</BarChart>
</Pane>
My controller atm:
package Controllers;
public class ControllerZW implements Interface.Ibacktomenu {
#Override
public void backtomenu() {
try {
Main.mainStage.setScene(
new Scene(FXMLLoader.load(getClass().getResource("../scenes/MainMenu.fxml")))
);
} catch (IOException e) {
System.out.println("Probably couldnt find resource file");
e.printStackTrace();
}
}
}
Do you want to render the data form database to XML format?
I think some template engine maybe help you, such as Freemarker.
You could use Freemarker as view of Spring MVC, please refer to http://viralpatel.net/blogs/spring-mvc-freemarker-ftl-example/
Use a Task to get the data from the database and any method described in Passing Parameters JavaFX FXML to get the data to the controller, e.g.
#Override
public void start(Stage primaryStage) {
Task<Pane> dataLoader = new Task<Pane>() {
#Override
protected Pane call() throws Exception {
// create data
XYChart.Series<String, Double> rain = new XYChart.Series<>();
rain.setName("rain");
Month[] months = Month.values();
for (Month month : months) {
// simulates slow database connection; just adding some random values here
Thread.sleep(200);
rain.getData().add(new XYChart.Data<>(month.toString(), Math.random() * 100));
}
// load chart (replace BarChartLoad.class.getResource("chart.fxml") with your own URL)
FXMLLoader loader = new FXMLLoader(BarChartLoad.class.getResource("chart.fxml"));
Pane pane = loader.load();
// pass data to controller
loader.<ControllerZW>getController().setData(FXCollections.observableArrayList(rain));
return pane;
}
};
// placeholder
Pane root = new Pane();
Scene scene = new Scene(root, 1000, 600);
// set new scene root on successfull completion of the task
dataLoader.setOnSucceeded(evt -> scene.setRoot(dataLoader.getValue()));
// TODO: handle task failure
new Thread(dataLoader).start();
primaryStage.setScene(scene);
primaryStage.show();
}
public class ControllerZW implements Interface.Ibacktomenu {
#FXML
private BarChart<String, Double> graph;
public void setData(ObservableList<XYChart.Series<String, Double>> data) {
graph.setData(data);
}
...

JavaFX initialize() is not called on inner custom component

I am using javaFX and I need to put custom components to my scene. Therefore I have "main_pane.fxml" with grid pane containing my components (for example DocumentModule).
main_pane.fxml
<GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="GUI.MainPane" gridLinesVisible="true" >
<padding>
<Insets bottom="0" top="0" left="0" right="0" />
</padding>
.
.
.
<DocumentModule fx:id="documentModule"
minWidth="200" minHeight="400"
GridPane.columnIndex="0" GridPane.rowIndex="1">
.
.
.
</DocumentModule>
.
.
.
Each of them is defined in separate fxml file.
document_module.fxml
<GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="GUI.DocumentModule" >
<padding>
<Insets bottom="0" top="0" left="0" right="0" />
</padding>
<ToolBar fx:id="toolBar" GridPane.rowIndex = "0" GridPane.columnIndex = "0" orientation="HORIZONTAL" >
.
.
.
</ToolBar>
<ScrollPane fx:id="scrollPane" hbarPolicy="AS_NEEDED" vbarPolicy="AS_NEEDED" GridPane.rowIndex = "1" GridPane.columnIndex = "0">
<DocumentView fx:id="documentView"/>
</ScrollPane>
</GridPane>
Problem is that DocumentModule is not initialized after construction. It's constructor is called but not it's initialize(URL location, ResourceBundle resources) method. Therefore objects from fxml are not injected.
Controller code for document module
public class DocumentModule extends GridPane implements Initializable {
protected Document document;
#FXML
private DocumentView documentView;
#FXML
private ScrollPane scrollPane;
.
.
.
public DocumentModule() {
System.out.println("Document Module constructed.");
//this is called correctly
}
#Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println("Document Module initialized.");
//This is not called at all.
}
}
For MainPane everything works fine but not for any of inner components.
The component tree seems to be constructed correctly, just the inner components are not initialized. Also the inner components are not dispalyed in application scene (if I load their fxml directly they work, if I use fx:include they are just displayed).
MainPane controller
public final class MainPane extends GridPane implements Initializable {
#FXML
private DocumentModule documentModule;
#FXML
private EditModule editModule;
public MainPane() {
System.out.println("Main Pane constructed.");
}
#Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println("Main Pane initialized.");
}
}
Application entry class' start method
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("main_pane.fxml"));
GridPane root = fxmlLoader.load();
Scene scene = new Scene(root, 800, 600);
primaryStage.setMaximized(true);
primaryStage.setMinWidth(800);
primaryStage.setMinHeight(600);
primaryStage.setTitle("App");
primaryStage.setScene(scene);
primaryStage.show();
}
I haven't find any topic/page/blog with same problem. Few of them had similar symptoms, but no of solutins helped me. Does anyone have idea why initialize is not called on inner components?
Thanks!
Tom
Nowhere in your code do you actually load document_module.fxml, so the elements defined there will never be created. The initialize() method is called on the controller for an fxml file when the FXMLLoader loads that file, but since you never load the fxml file, the initialize() method is never called.
The element <DocumentModule> in your main FXML merely causes the DocumentModule class to be instantiated (via a call to its no-arg constructor), but there is no link from there to the fxml file.
It looks like you are trying to follow the FXML custom component pattern. To do so, you need to load the FXML file in the custom component constructor. Specify a dynamic root and do not specify the controller class in the fxml, and set both on the FXMLLoader before you call load:
document_module.fxml:
<fx:root type="GridPane" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="0" top="0" left="0" right="0" />
</padding>
<ToolBar fx:id="toolBar" GridPane.rowIndex = "0" GridPane.columnIndex = "0" orientation="HORIZONTAL" >
.
.
.
</ToolBar>
<ScrollPane fx:id="scrollPane" hbarPolicy="AS_NEEDED" vbarPolicy="AS_NEEDED" GridPane.rowIndex = "1" GridPane.columnIndex = "0">
<DocumentView fx:id="documentView"/>
</ScrollPane>
</fx:root>
DocumentModule.java:
public class DocumentModule extends GridPane implements Initializable {
protected Document document;
#FXML
private DocumentView documentView;
#FXML
private ScrollPane scrollPane;
.
.
.
public DocumentModule() {
System.out.println("Document Module constructed.");
FXMLLoader loader = new FXMLLoader(getClass().getResource("document_module.fxml"));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException exc) {
exc.printStackTrace();
// this is pretty much fatal, so:
System.exit(1);
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println("Document Module initialized.");
// This will now be called after the #FXML-annotated fields are initialized.
}
}

Categories