i'm new at JavaFx and actually i'm trying to change/set a label text. In my eyes i did everything that is to do but it does not work. Hope that someone could help me. I'm searching now for hours but guess i'll never find my misstake. Furthermore i guess the misstake is to find at the controller but in some examples i watched, it looks similar to my code..
The FXML:
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<SplitPane id="Split" dividerPositions="0.3" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="620.0" prefWidth="871.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="presentation.DataController">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Label id ="name" fx:id="name" layoutX="26.0" layoutY="21.0" prefHeight="576.0" prefWidth="205.0" textAlignment="CENTER"/>
</children></AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Label fx:id="daten" layoutX="19.0" layoutY="14.0" prefHeight="576.0" prefWidth="511.0"/>
<!-- <Slider layoutX="573.0" layoutY="11.0" orientation="VERTICAL" prefHeight="576.0" prefWidth="14.0" /> !-->
</children></AnchorPane>
</items>
</SplitPane>
My Controller:
package presentation;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class DataController implements Initializable {
#FXML
private Label name;
private Label daten;
private void init() {
name.setText("Hello World!");
daten.setText("AnotherTest");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
And the Application Class
package presentation;
import application.*;
import data.*;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class Data extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("DataView.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Übung 0");
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
public class DataController implements Initializable {
#FXML
private Label name;
#FXML
private Label daten;
private void init() {
name.setText("Hello World!");
daten.setText("AnotherTest");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
init();
}
}
You need to load your fxml file in the controller class. Something like this:
Parent warningRoot = FXMLLoader.load(getClass().getResource("yourfxmlfile.fxml"));
Scene scene = new Scene(warningRoot);
Stage stage = new Stage();
Related
my program is really simple, it's just two scenes, the second scene just says "this is the second scene" and in the first scene you have to select a folder which will then appear in an un-editable TextField. you can go back and forth between these two scenes using a back and next button. The problem is, when I go to the second scene then go back to the first scene, the TextField is empty, when it should really be displaying the path of the folder I selected before, but when I reselect the folder it shows again, but every time I change the scene, it disappears. The FX:id of the TextField is textfield. I tried making it so that it again sets the text to the path of the folder but this time in the "back" method for the back button. But this never works if it's not in the OpenFolder method for some reason, it doesn't work anywhere else. I want to know why it only works in the OpenFolder method and not anywhere else. This is my controller class:
package sample;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import javafx.event.ActionEvent;
public class Controller {
private Stage stage;
private Scene scene;
private Parent root;
public Controller() {
}
#FXML
public void Next(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("scene2.fxml"));
stage = ((Stage)((Node)event.getSource()).getScene().getWindow());
scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#FXML
public void Back(ActionEvent event) throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("scene1.fxml"));
stage = ((Stage)((Node)event.getSource()).getScene().getWindow());
scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#FXML
private TextField textfield = new TextField();
#FXML
private AnchorPane anchorid1;
#FXML
public static File thefolder;
#FXML
DirectoryChooser SongsOpener;
#FXML
public File OpenFolder(){
final DirectoryChooser SongsOpener = new DirectoryChooser();
stage = (Stage) anchorid1.getScene().getWindow();
thefolder = SongsOpener.showDialog(stage);
if (thefolder != null){
textfield.setText(thefolder.getAbsolutePath());
}
return thefolder;
}
}
This is my FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane fx:id="anchorid1" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="411.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<Text layoutX="326.0" layoutY="76.0" strokeType="OUTSIDE" strokeWidth="0.0" text="This is scene 1">
<font>
<Font size="33.0" />
</font>
</Text>
<Button layoutX="939.0" layoutY="373.0" mnemonicParsing="false" onAction="#Next" text="Next" />
<Label layoutX="63.0" layoutY="117.0" prefHeight="18.0" prefWidth="122.0" text="Songs folder :">
<font>
<Font size="20.0" />
</font></Label>
<Button layoutX="888.0" layoutY="121.0" mnemonicParsing="false" onAction="#OpenFolder">
<graphic>
<ImageView fitHeight="18.0" fitWidth="17.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../toppng.com-folder-icon-png-transparent-black-and-white-folder-ico-1589x1366.png" />
</image>
</ImageView>
</graphic>
</Button>
<TextField fx:id="textfield" layoutX="190.0" layoutY="119.0" prefHeight="23.0" prefWidth="685.0" />
</children>
</AnchorPane>
Here it shows the path in the TextField after I selected the folder image
But when I go to the next scene and come back, it disappears : image
Every time you load a FXML file, the FXMLLoader creates a new set of controls corresponding to the elements in the FXML file. So if you reload the first view, you get a new TextField, which obviously does not contain the same text as the previous TextField unless you explicitly set the text.
So you can either avoid reloading the FXML files every time (first solution below), or create a mechanism by which you update the text field with the correct value every time you load the FXML (second solution below, using a model and binding to update the text field).
Note also that there's absolutely no reason to create new Scenes every time you change the view. Just use a single scene and replace its root.
Solution loading each view once
So one way to fix this is to just load each FXML once, and then arrange to switch between the two views in the controllers:
App.java:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class App extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader loader1 = new FXMLLoader(getClass().getResource("View1.fxml"));
Parent view1 = loader1.load();
FXMLLoader loader2 = new FXMLLoader(getClass().getResource("View2.fxml"));
Parent view2 = loader2.load();
Scene scene = new Scene(view1);
View1Controller controller1 = loader1.getController();
View2Controller controller2 = loader2.getController();
controller1.setOnNext(() -> scene.setRoot(view2));
controller2.setOnBack(() -> scene.setRoot(view1));
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
View1Controller.java:
import java.io.File;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
public class View1Controller {
private Runnable onNext = () -> {};
#FXML
private TextField textfield ;
public void setOnNext(Runnable onNext) {
this.onNext = onNext ;
}
#FXML
private void next() {
onNext.run();
}
#FXML
public void openFolder(){
final DirectoryChooser songsOpener = new DirectoryChooser();
Stage stage = (Stage) textfield.getScene().getWindow();
File thefolder = songsOpener.showDialog(stage);
if (thefolder != null){
textfield.setText(thefolder.getAbsolutePath());
}
}
}
View2Controller.java
import javafx.fxml.FXML;
public class View2Controller {
private Runnable onBack = () -> {} ;
public void setOnBack(Runnable onBack) {
this.onBack = onBack ;
}
#FXML
private void back() {
onBack.run();
}
}
View1.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="411.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.switchviews.View1Controller">
<children>
<Text layoutX="326.0" layoutY="76.0" strokeType="OUTSIDE" strokeWidth="0.0" text="This is scene 1">
<font>
<Font size="33.0" />
</font>
</Text>
<Button layoutX="939.0" layoutY="373.0" mnemonicParsing="false" onAction="#next" text="Next" />
<Label layoutX="63.0" layoutY="117.0" prefHeight="18.0" prefWidth="122.0" text="Songs folder :">
<font>
<Font size="20.0" />
</font></Label>
<Button layoutX="888.0" layoutY="121.0" mnemonicParsing="false" onAction="#openFolder" text="Open" />
<TextField fx:id="textfield" layoutX="190.0" layoutY="119.0" prefHeight="23.0" prefWidth="685.0" />
</children>
</AnchorPane>
View2.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<HBox alignment="CENTER" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.switchviews.View2Controller">
<children>
<Button mnemonicParsing="false" onAction="#back" text="Back" />
</children>
</HBox>
Solution using a model to store the state
Another way is to create a model containing the data you need, and bind, for example, the text in the text field to an appropriate property in the model. Then just pass the same model instance to each controller.
For example:
ViewState.java:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class ViewState {
private final StringProperty selectedFolder = new SimpleStringProperty();
public final StringProperty selectedFolderProperty() {
return this.selectedFolder;
}
public final String getSelectedFolder() {
return this.selectedFolderProperty().get();
}
public final void setSelectedFolder(final String selectedFolder) {
this.selectedFolderProperty().set(selectedFolder);
}
}
Then in App.java:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class App extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader loader1 = new FXMLLoader(getClass().getResource("View1.fxml"));
Parent view1 = loader1.load();
ViewState viewState = new ViewState();
View1Controller controller = loader1.getController();
controller.setViewState(viewState);
Scene scene = new Scene(view1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
View1Controller.java:
import java.io.File;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
public class View1Controller {
#FXML
private TextField textfield ;
private ViewState viewState ;
public void setViewState(ViewState viewState) {
this.viewState = viewState ;
textfield.textProperty().bindBidirectional(viewState.selectedFolderProperty());
}
#FXML
private void next() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View2.fxml"));
Parent root = loader.load();
textfield.getScene().setRoot(root);
View2Controller controller = loader.getController();
controller.setViewState(viewState);
}
#FXML
public void openFolder(){
final DirectoryChooser songsOpener = new DirectoryChooser();
Stage stage = (Stage) textfield.getScene().getWindow();
File thefolder = songsOpener.showDialog(stage);
if (thefolder != null){
textfield.setText(thefolder.getAbsolutePath());
}
}
}
View2Controller:
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
public class View2Controller {
private ViewState viewState ;
#FXML
private Parent root ;
public void setViewState(ViewState viewState) {
this.viewState = viewState;
}
#FXML
private void back() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View1.fxml"));
Parent newRoot = loader.load();
this.root.getScene().setRoot(newRoot);
View1Controller controller = loader.getController();
controller.setViewState(viewState);
}
}
The only change to the FXML files is adding a fx:id to the root element in the second view:
View2.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<HBox fx:id="root" alignment="CENTER" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.switchviews.View2Controller">
<children>
<Button mnemonicParsing="false" onAction="#back" text="Back" />
</children>
</HBox>
if i run the Main Class my JavaFX is not opening. A Java symbol appears in my menubar but no window appears. I don't get any error in the console. I am on a Mac machine and I am using eclipse, together with e(fx)clipes and Gluon Scene Builder. The code is actually 1 by 1 written down from a tutorial. In the tutorial the run configuration JRE is set to JavaSE-1.8. - however, I tested every available JRE, without any success. What do I need to adjust?
Main Class:
package application;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
public class Main extends Application {
private Stage primaryStage;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
}
public void mainWindow() {
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("MainWindow.fxml"));
AnchorPane pane = loader.load();
primaryStage.setMinHeight(400.00);
primaryStage.setMinWidth(500.00);
MainWindowController mainWindowController = loader.getController();
mainWindowController.setMain(this);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Controller class:
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class MainWindowController {
public Main main;
#FXML private Label label;
#FXML private TextField field;
#FXML private Button clear;
#FXML private Button changeText;
public void setMain(Main main) {
// TODO Auto-generated method stub
this.main = main;
}
#FXML
public void handleChangeText() {
String text = field.getText();
label.setText(text);
}
#FXML
public void handleClear() {
field.clear();
}
}
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="400.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainWindowController">
<children>
<VBox alignment="CENTER" layoutY="83.0" spacing="30.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="label" text="Label">
<font>
<Font size="24.0" />
</font></Label>
<HBox alignment="CENTER">
<children>
<TextField fx:id="field" prefWidth="220.0" />
</children>
</HBox>
<HBox alignment="CENTER" spacing="20.0">
<children>
<Button fx:id="changeText" mnemonicParsing="false" onAction="#handleChangeText" text="Change Text" />
<Button fx:id="clear" mnemonicParsing="false" onAction="#handleClear" text="Clear" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
You didn't do anything in you start method. All you did was set your primaryStage variable, you should call your mainWindow method after that.
I am using the AnimateFX library, which is just a compilation of node animations.
AnimationFX fx = new ZoomOut(background);
fx.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
//background.setScaleX(1.0D);
//background.setScaleY(1.0D);
//background.setScaleZ(1.0D);
//background.setOpacity(1.0D);
// -- some wait function --
Stage stage = (Stage) background.getScene().getWindow();
stage.setIconified(true);
}
});
fx.play();
This block results in the window minimizing, but now the window is both transparent and minuscule.
If I uncomment the background node transformations and comment stage.setIconified(true), the animation ends with the window returning to full size, as expected.
However, if I uncomment the background node transformations without commenting stage.setIconified(true), the program minimizes without running the background node transformations.
I assumed it was some synchronizing issue, but adding a wait function in the // -- some wait function -- locale simply resulted in the program waiting without the background node transformations running, and then minimizing.
A bit confused as to why this happens.
EDIT
Here is some runnable code that replicates the problem. I find that if I replace the ImageView with a Button and change the event to that of a button action, then the problem no longer occurs.
package app;
import animatefx.animation.AnimationFX;
import animatefx.animation.ZoomOut;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Controller {
#FXML
VBox background;
#FXML
ImageView minimize;
#FXML
public void onMinimizeClicked(MouseEvent mouseEvent) {
AnimationFX fx = new ZoomOut(background);
fx.setSpeed(0.75D);
fx.setResetOnFinished(true);
Stage stage = (Stage) background.getScene().getWindow();
fx.setOnFinished(actionEvent -> stage.setIconified(true));
fx.play();
}
}
Edit #2
Here is my main class
package app;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
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("sample.fxml"));
primaryStage.initStyle(StageStyle.TRANSPARENT);
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is my sample.fxml file
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="background" prefHeight="500.0" prefWidth="400.0" style="-fx-background-color: transparent" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="app.Controller">
<AnchorPane fx:id="content" prefHeight="464.0" prefWidth="500.0" style="-fx-background-color: #3D4956
" VBox.vgrow="ALWAYS">
<children>
<ImageView fx:id="minimize" fitHeight="150.0" fitWidth="200.0" layoutX="100.0" layoutY="175.0" onMouseClicked="#onMinimizeClicked" pickOnBounds="true" preserveRatio="true" />
</children>
</AnchorPane>
</VBox>
I don't know the cause of this behaviour but I managed to solve it by adding an image inside the ImageView in the FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="background" prefHeight="500.0" prefWidth="400.0" style="-fx-background-color: transparent" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.Controller">
<AnchorPane fx:id="content" prefHeight="464.0" prefWidth="500.0" style="-fx-background-color: #3D4956
" VBox.vgrow="ALWAYS">
<children>
<ImageView fx:id="minimize" fitHeight="150.0" fitWidth="200.0" layoutX="100.0" layoutY="175.0" onMouseClicked="#onMinimizeClicked" pickOnBounds="true" preserveRatio="true" >
<Image url="file:image/iamge.png"/>
</ImageView>
</children>
</AnchorPane>
</VBox>
I'm trying to make a program for generating a PieChart. At the first Window
you can write a name (X) and the percentage (Y). When you press Insert, this information will be written in two arrays one for X (XI) and the second for Y (YI).
When you press stop, the program writes this information in an observable list.
But it wasn't work, I think the problem is at the declaration of the arrays. But don't know the specific problem can anyone help me?
MainClass.java :
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainClass extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("FirstPage.fxml"))));
primaryStage.show();}}
Controller.java :
package application;
import java.io.IOException;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class Controller{
#FXML
private TextField XInput;
#FXML
private TextField YInput;
#FXML
private PieChart PieChart;
public String[] XI; // Array for X
public int [] YI; // Array for Y
public int atm = -1;
#FXML
void GetValue(ActionEvent event) { // Write in Arrays
atm++;
XI[atm] = XInput.getText();
YI[atm] = Integer.parseInt(YInput.getText());
}
#FXML
void Stop(ActionEvent event) throws IOException {
// Load new Stage
Stage primaryStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("SecondPage").openStream());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
// Create ObservableList
ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();
int i = 0;
while(i == atm){
pieChartData.add(new PieChart.Data(XI[i], YI[i]));
i++;
}
// Set PieChart
PieChart.setData(pieChartData);
}
}
FirstPage.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="410.0" prefWidth="592.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<Button layoutX="108.0" layoutY="235.0" mnemonicParsing="false" onAction="#GetValue" prefHeight="40.0" prefWidth="376.0" style="-fx-background-color: #FFF; -fx-border-color: #000;" text="Insert" />
<TextField fx:id="YInput" alignment="CENTER" layoutX="108.0" layoutY="154.0" prefHeight="72.0" prefWidth="376.0" promptText="Y" style="-fx-background-color: #FFF; -fx-border-color: #000;">
<font>
<Font size="30.0" />
</font>
</TextField>
<TextField fx:id="XInput" alignment="CENTER" layoutX="108.0" layoutY="74.0" prefHeight="72.0" prefWidth="376.0" promptText="X" style="-fx-background-color: #FFF; -fx-border-color: #000;">
<font>
<Font size="30.0" />
</font>
</TextField>
<Button layoutX="108.0" layoutY="282.0" mnemonicParsing="false" onAction="#Stop" prefHeight="40.0" prefWidth="376.0" style="-fx-background-color: #FFF; -fx-border-color: #000;" text="Stop" />
</children>
</AnchorPane>
SecondPage :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="571.0" prefWidth="744.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<PieChart fx:id="PieChart" layoutY="7.0" prefHeight="564.0" prefWidth="744.0" />
</children>
</AnchorPane>
I know this question is asked many times, but I can't find a solution, that works for me (actually I can't even see what I am doing wrong).
The basic idea is to load GUI-components when needed. So I structured the GUI in various FXML-Files and implemented controller-classes. Both - FXML-files and classes - are stored in the same package but ther is a package for every component. Every FXML-file is loading and added to the GUI as long as I do not define the controller-class within the FXML-file (fx:controller). If it is defined I will get a LoadException.
For a better understanding here is my code (simplified):
Main.java:
package application;
import application.a.ControllerA;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application
{
// Button aus MainLayout.fxml
#FXML
private Button button;
#Override
public void start(Stage primaryStage)
{
try
{
BorderPane root = new BorderPane();
Parent contentMain = FXMLLoader.load(getClass().getResource("MainLayout.fxml"));
ControllerA contentA = new ControllerA(root);
root.setTop(contentA.getContent());
root.setCenter(contentMain);
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
launch(args);
}
// Event-Handler für den Button -> funktioniert!
#FXML
public void buttonClicked(ActionEvent e)
{
if (!button.getText().equals("NEW"))
{
button.setText("NEW");
}
else
{
button.setText("OLD");
}
}
}
This class is also a controller for the following layout (and it works fins so far):
MainLayout.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.GridPane?>
<Pane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Main">
<children>
<Button fx:id="button" mnemonicParsing="false" onAction="#buttonClicked" text="Button" />
</children>
</Pane>
In a sub-package (called a) of application you will find this:
ControllerA.java:
package application.a;
import java.net.URL;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
public class ControllerA
{
private Parent content;
#FXML
private Button buttonA;
public ControllerA(BorderPane root)
{
String sceneFile = "A.fxml";
URL url = null;
try
{
url = getClass().getResource(sceneFile);
content = FXMLLoader.load(url);
}
catch (Exception ex)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Parent getContent()
{
return content;
}
#FXML
public void clickedA(ActionEvent e)
{
buttonA.setText("Clicked already");
}
}
A.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.GridPane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.a.ControllerA">
<children>
<Button fx:id="buttonA" layoutX="274.0" layoutY="188.0" mnemonicParsing="false" onAction="#clickedA" text="A" />
</children>
</Pane>
And this is, where it all went wrong:
javafx.fxml.LoadException:
/Z:/BachelorArbeit/Projektdateien/Entwicklung/EclipseWorkspace/Sandbox/bin/application/a/A.fxml:8
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:103)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:932)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
at application.a.ControllerA.<init>(ControllerA.java:26)
at application.Main.start(Main.java:35)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.InstantiationException: application.a.ControllerA
at java.lang.Class.newInstance(Class.java:427)
at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:927)
... 23 more
Caused by: java.lang.NoSuchMethodException: application.a.ControllerA.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 25 more
I've tried to fiddle around with the path-String like
"./a/A.fxml"
"/application/a/A.fxml"
"A.fxml"
"a/A.fxml"
...
but nothing worked. I would be quite relieved if someone can halp me with this problem.
You're mixing two different ways of using FXML and controllers. If your FXML file has fx:controller="SomeClass", then the FXMLLoader will instantiate that class and use the instance as a controller: in other words, it makes the FXML create the controller.
On the other hand, your controller's constructor loads the FXML, so you also have the controller creating the UI defined in the FXML.
The reason for the exception is that when the FXMLLoader encounters the fx:controller attribute, it calls the no-argument constructor of the specified class: i.e. in this case it tries to call new ControllerA(). Since there is no such constructor you get the exception:
java.lang.NoSuchMethodException: application.a.ControllerA.<init>()
It's not really clear what the purpose of the BorderPane parameter to the controller's constructor is, as you never use it. However, even if you had an appropriate constructor here, it would cause another problem: loading the FXML would invoke the controller's constructor, which loads the FXML, which would invoke the controller's constructor, etc: you would get a StackOverflowException. If you want to load the FXML from the controller's constructor:
remove the fx:controller attribute from the FXML file
Explicitly set the controller on the FXMLLoader to the current controller instance:
public ControllerA(BorderPane root)
{
String sceneFile = "A.fxml";
URL url = null;
try
{
url = getClass().getResource(sceneFile);
FXMLLoader loader = new FXMLLoader(url);
loader.setController(this);
content = loader.load();
}
catch (Exception ex)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I'll show you an minimal working example project and I suggest that you adapt your structure to it.
The folder structure is as follows
Here is the main class
package dynamic.content.javafx;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public final class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));
Scene scene = new Scene(root, 300, 200);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The main controller
package dynamic.content.javafx;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
public final class MainController implements Initializable{
#FXML
private Button btnSwitch;
#FXML
private AnchorPane contentPane;
#Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
private boolean swtch = false;
#FXML
public void handleContentSwitch() throws IOException{
Parent contentNode = null;
if(swtch){
System.out.println("loading content A");
contentNode = FXMLLoader.load(getClass().getResource("./content/content_a.fxml"));
}else{
System.out.println("loading content B");
contentNode = FXMLLoader.load(getClass().getResource("./content/content_b.fxml"));
}
contentPane.getChildren().clear();
contentPane.getChildren().add(contentNode);
swtch = !swtch;
}
}
The main FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dynamic.content.javafx.MainController">
<children>
<Button fx:id="btnSwitch" onAction="#handleContentSwitch" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" text="Switch Content" />
<AnchorPane fx:id="contentPane" prefHeight="150.0" prefWidth="300.0" />
</children>
</VBox>
Let's say we have content A and B we need to create a controller and a FXML every one of them
package dynamic.content.javafx.content;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class ContentAController implements Initializable{
#FXML
private Label labl;
#Override
public void initialize(URL location, ResourceBundle resources) {
labl.setText("Content A");
}
}
And now the corresponding FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dynamic.content.javafx.content.ContentAController">
<children>
<Label fx:id="labl" alignment="CENTER" contentDisplay="CENTER" text="Label" textAlignment="CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
Here follows the second controller
package dynamic.content.javafx.content;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class ContentBController implements Initializable{
#FXML
private Label labl;
#Override
public void initialize(URL location, ResourceBundle resources) {
labl.setText("Content B");
}
}
and the second FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dynamic.content.javafx.content.ContentBController">
<children>
<Label fx:id="labl" alignment="CENTER" contentDisplay="CENTER" text="Label" textAlignment="CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
If you know press the button the content will be loaded dynamically
If you have any questions, just let me know :-)