I'm really struggling to understand JavaFX controllers, my aim is to write to a TextArea to act as a log.
My code is below, but I want to be able to change values ETC from another class that I can call when needed. I have tried to create a controller class that extents Initializable but i cant get it to work. Could some one steer me in the correct direction?
I want to move the #FXML code at the bottom to another class and it update the Scene.
package application;
import javafx.event.ActionEvent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
Scene scene = new Scene(root,504,325);
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);
}
public Thread thread = new Thread(new webimporter());
#FXML
public Label runningLabel;
#FXML
public TextArea txtArea;
#FXML
void runClick(ActionEvent event) throws IOException{
changeLabelValue("Importer running...");
thread.start();
}
#FXML
protected void stopClick(ActionEvent event){
changeLabelValue("Importer stopped...");
thread.interrupt();
}
#FXML
void changeLabelValue(String newText){
runningLabel.setText(newText);
}
void changeTextAreaValue(String newText1){
txtArea.setText(newText1);
}
}
Don't make the Application class a controller. It's a sin. There are other questions and answers which address this, but my search skills cannot find them at this time.
The reason it is a sin is:
You are only supposed to have one Application instance, and, by default, the loader will make a new instance, so you end up with two application objects.
Referencing the member objects is confusing, because the original launched application doesn't have the #FXML injected fields, but the loader created application instance does have #FXML inject fields.
Also, unrelated advice: Don't start trying to write multi-threaded code until you have the application at least working to the extent where it displays your UI.
A multi-threaded logger for JavaFX is in the answer to Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks, though unfortunately it is not straight-forward in its implementation and comes with little documentation.
textlogger/Root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="400.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="textlogger.ImportController">
<children>
<HBox alignment="BASELINE_LEFT" minHeight="-Infinity" minWidth="-Infinity" spacing="10.0">
<children>
<Button mnemonicParsing="false" onAction="#run" text="Run" />
<Button mnemonicParsing="false" onAction="#stop" text="Stop" />
<Label fx:id="runningLabel" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</HBox>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
textlogger.ImportController.java
package textlogger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
public class ImportController {
#FXML
private Label runningLabel;
#FXML
private TextArea textArea;
private WebImporter importer;
#FXML
void run(ActionEvent event) throws IOException {
changeLabelValue("Importer running...");
if (importer == null) {
importer = new WebImporter(textArea);
Thread thread = new Thread(
importer
);
thread.setDaemon(true);
thread.start();
}
}
#FXML
void stop(ActionEvent event){
changeLabelValue("Importer stopped...");
if (importer != null) {
importer.cancel();
importer = null;
}
}
private void changeLabelValue(String newText){
runningLabel.setText(newText);
}
}
textlogger.WebImporter.java
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.TextArea;
import java.time.LocalTime;
public class WebImporter extends Task<Void> {
private final TextArea textArea;
public WebImporter(TextArea textArea) {
this.textArea = textArea;
}
#Override
protected Void call() throws Exception {
try {
while (!isCancelled()) {
Thread.sleep(500);
Platform.runLater(
() -> textArea.setText(
textArea.getText() + LocalTime.now() + "\n"
)
);
}
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
}
textlogger.TextLoggingSample.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TextLoggingSample extends Application {
#Override
public void start(Stage stage) {
try {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(
getClass().getResourceAsStream(
"Root.fxml"
)
);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Related
I'm currently trying to figure out how to implement a feature in which the user clicks a button and a popup appears, and then the user enters data into that popup, clicks a confirm button, and the data becomes visible in the popups owner. I was able to make data from the owner stage carry over to the popup stage after follow Bro Code's tutorial on making controllers communicate, however getting it to work the other way around is proving troublesome. Below is a project in which I've isolated the issue to try and figure it out.
App.java
package org.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.Objects;
public class App extends Application {
public static Stage stage;
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("primary.fxml")));
Scene scene = new Scene(root);
stage = new Stage();
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
PrimaryController.java
package org.example;
import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class PrimaryController {
#FXML
Label label;
#SuppressWarnings("unused")
public void login(ActionEvent event) throws IOException{
FXMLLoader loader = new FXMLLoader(getClass().getResource("secondary.fxml"));
Parent root = loader.load();
SecondaryController secondaryController = loader.getController();
secondaryController.stage = new Stage();
secondaryController.stage.initModality(Modality.APPLICATION_MODAL);
secondaryController.stage.initOwner(App.stage);
Scene scene = new Scene(root);
secondaryController.stage.setScene(scene);
secondaryController.stage.show();
}
public void displayMessage(String message){
label.setText(message);
}
}
SecondaryController.java
package org.example;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.io.IOException;
public class SecondaryController {
#FXML
TextField textField;
public Stage stage;
#SuppressWarnings("unused")
public void writeToOwner(ActionEvent event) throws IOException {
String message = textField.getText();
FXMLLoader loader = new FXMLLoader(getClass().getResource("primary.fxml"));
Parent root = loader.load();
PrimaryController primaryController = loader.getController();
primaryController.displayMessage(message);
stage.close();
}
}
primary.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.PrimaryController">
<children>
<Button layoutX="270.0" layoutY="230.0" mnemonicParsing="false" onAction="#login" text="Login" />
<Label fx:id="label" layoutX="280.0" layoutY="191.0" />
</children>
</AnchorPane>
secondary.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.SecondaryController">
<children>
<TextField fx:id="textField" layoutX="219.0" layoutY="187.0" onAction="#writeToOwner" />
</children>
</AnchorPane>
The current behavior that I get from this code is almost what I want, except when the user would submit the text, it closes the popup but doesn't change the popups owner.
In your SecondaryController, it looks like you're creating a second instance of PrimaryController, leaving the first unchanged when invoking writeToOwner().
One approach is to arrange for the controllers to see a common model, much as they share a common stage in your example. The simplest such model is a single ObservableValue, illustrated here. To see the effect,
Add a StringProperty named text to your SecondaryController and make it accessible. In writeToOwner(), simply update the text and the bound label will follow.
public class SecondaryController {
#FXML
TextField textField;
public Stage stage;
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text;
}
#SuppressWarnings("unused")
public void writeToOwner(ActionEvent event) throws IOException {
text.set(textField.getText());
stage.close();
}
}
In your PrimaryController, bind the label's textProperty() to the textProperty() of the SecondaryController.
public class PrimaryController {
#FXML
Label label;
public StringProperty text = new SimpleStringProperty();
#SuppressWarnings("unused")
public void login(ActionEvent event) throws IOException {
…
secondaryController.stage.initOwner(App.stage);
label.textProperty().bind(secondaryController.textProperty());
…
}
}
In practice, you'll want to avoid public access to class members.
I am new to Java and JavaFX. I've created a JavaFX project using SceneBuilder/FXML and I'm attempting to add a realtime clock at the start of the program which works throughout the whole program at the top of the screen. I created a text area and tried to add the clock function from the code we were given but every time I start the program, it always shows up blank. Even doing trying to just use the .setText("string") function manually doesn't work so I think I'm putting the code in the wrong place. If possible, can someone tell me where this code should go or point me in the correct direction?
Here is my main:
package application;
import java.lang.ModuleLayer.Controller;
import java.util.Date;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
public class Main extends Application {
public static Stage stage = null;
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/Ui.fxml"));
Scene scene = new Scene(root);
stage.initStyle(StageStyle.UNDECORATED);
stage.setScene(scene);
this.stage = stage;
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is my controller code:
package application;
import java.util.Date;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
public class UiController {
#FXML
private TextArea clockTextArea;
#FXML
private TextArea transactionLog;
#FXML
private TextField recipentField;
#FXML
private Button payButton;
#FXML
private Button requestButton;
#FXML
private TextField commentField;
private void refreshClock()
{
Thread refreshClock = new Thread()
{
public void run()
{
while (true)
{
Date dte = new Date();
String topMenuStr = " " + dte.toString();
clockTextArea.setText(topMenuStr);
try
{
sleep(3000L);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} // end while ( true )
} // end run thread
};
refreshClock.start();
}
public void initialize() {
TextArea clockTextArea = new TextArea();
refreshClock();
}
}
It should be noted that whether it is in Swing or JavaFX, when updating the control, it needs to be done in the event dispatching thread, EDT, otherwise it will not work or throw Exception Not on FX application thread; currentThread = *
At this time, you only need to place the code of the update control code in the event dispatch thread for completion. Use the following code in JavaFX
Platform.runLater(new Runnable() {
#Override
public void run() {
//Update the control code of JavaFX and put it here
}
});
So change your code:
Date dte = new Date();
String topMenuStr = " " + dte.toString();
Platform.runLater(() -> clockTextArea.setText(topMenuStr));
and check if it works.
One route is to use Timeline to handle the clock. It's will be independent of your other code.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
/**
* JavaFX App
*/
public class App extends Application {
#Override
public void start(Stage stage) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("primary.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 1080, 720);
stage.setScene(scene);
stage.show();
} catch (IOException ex) {
System.out.println(ex.toString());
}
}
public static void main(String[] args) {
launch();
}
}
PrimaryController
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.util.Duration;
public class PrimaryController {
#FXML Label lblClock;
Timeline clockHandler;
#FXML
void initialize()
{
Locale locale_en_US = Locale.US ;
DateTimeFormatter formatterUS = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT ).withLocale( locale_en_US ) ;
clockHandler = new Timeline(
new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {
LocalTime currentTime = LocalTime.now();
String outputUs = currentTime.format(formatterUS);
lblClock.setText(outputUs);
}));
clockHandler.setCycleCount(Timeline.INDEFINITE);
clockHandler.play();
}
}
Primary FXML
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="TOP_CENTER" prefHeight="720.0" prefWidth="1080.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sed.home.javafxrealtimeclock.PrimaryController">
<children>
<MenuBar>
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<HBox>
<children>
<Label maxWidth="1.7976931348623157E308" HBox.hgrow="ALWAYS" />
<Label fx:id="lblClock" text="00:00">
<HBox.margin>
<Insets />
</HBox.margin>
</Label>
</children>
<VBox.margin>
<Insets left="5.0" right="5.0" />
</VBox.margin>
</HBox>
<AnchorPane prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<children>
<Label layoutX="451.0" layoutY="331.0" text="The other parts of the program here" />
</children>
</AnchorPane>
</children>
</VBox>
I got a Button called "Transfer" with the onclick method and a VBox with 3 Buttons in one window.
The second window only has a HBox container.
I wanna understand how to send the VBox to the HBox in the other window by pressing the transfer Button.
And what would also help is feedback regarding my code/file organization.
Main Class:
package testproject;
import javafx.application.Application;
import testproject.screen1.Screen1;
import testproject.screen2.Screen2;
import javafx.stage.Stage;
public class Main extends Application {
private Screen1 screen1;
private Screen2 screen2;
#Override
public void start(Stage primaryStage) throws Exception{
screen1 = new Screen1(this, new Stage());
screen2 = new Screen2(this, primaryStage);
}
public static void main(String[] args) {
Application.launch(args);
}
}
Screen1 Class:
package testproject.screen1;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import testproject.Main;
import java.io.IOException;
public class Screen1 {
public Screen1(Main main, Stage stage){
FXMLLoader loader = new FXMLLoader();
try {
Parent root =
loader.load(getClass().getResourceAsStream("/testproject/screen1/screen1.fxml"));
stage.setTitle("Screen 1");
stage.setScene(new Scene(root, 300, 275));
}
catch (IOException e) {
e.printStackTrace();
}
stage.show();
}
}
Screen2 Class:
package testproject.screen2;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import testproject.Main;
import java.io.IOException;
public class Screen2 {
public Screen2(Main main, Stage stage) {
FXMLLoader loader = new FXMLLoader();
try {
Parent root =
loader.load(getClass().getResourceAsStream("/testproject/screen2/screen2.fxml"));
stage.setTitle("Screen 2");
stage.setScene(new Scene(root, 300, 275));
}
catch (IOException e) {
e.printStackTrace();
}
stage.show();
}
}
Screen1Controller Class:
package testproject.screen1;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
public class Screen1Controller {
#FXML
private AnchorPane Pane1;
#FXML
private VBox VBoxScreen1;
#FXML
private Button TransferButton;
#FXML
void transferToScreen2(MouseEvent event){
}
}
Screen2Controller Class:
package testproject.screen2;
import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
public class Screen2Controller {
#FXML
private AnchorPane Pane2;
#FXML
private HBox HBoxScreen2;
}
One way I can think of is to pass a callback/consumer to the screen2 controller to let it know what to do with its node. Having said that, there can be many other approaches to this requirement.
As per my view, you dont need separate classes to load the screens. You can check the below working demo.
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader2 = new FXMLLoader(getClass() .getResource("screen2.fxml"));
VBox screen2 = loader2.load();
Screen2Controller screen2Controller = loader2.getController();
Stage screen2Stage = new Stage();
Scene scene2 = new Scene(screen2);
screen2Stage.setScene(scene2);
screen2Stage.setTitle("Screen 2");
screen2Stage.setX(900);
screen2Stage.setY(100);
screen2Stage.show();
FXMLLoader loader1 = new FXMLLoader(getClass() .getResource("screen1.fxml"));
VBox screen1 = loader1.load();
Screen1Controller screen1Controller = loader1.getController();
// Set a consumer to the screen1 to let it know what to do
screen1Controller.setTransferer(screen2Controller::moveNode);
Scene scene1 = new Scene(screen1);
primaryStage.setScene(scene1);
primaryStage.setTitle("Screen 1");
primaryStage.setX(100);
primaryStage.setY(100);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Screen1Controller.java
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import java.util.function.Consumer;
public class Screen1Controller {
#FXML
private VBox pane1;
#FXML
private VBox vBoxScreen1;
#FXML
private Button transferButton;
private Consumer<Node> transferer;
#FXML
void transferToScreen2(ActionEvent event) {
// First remove the node from the parent.
pane1.getChildren().remove(vBoxScreen1);
// Then send the node to do the other operation.
this.transferer.accept(vBoxScreen1);
}
public void setTransferer(Consumer<Node> transferer) {
this.transferer = transferer;
}
}
screen1.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox fx:id="pane1" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.stackoverflow.javafx.issue7.Screen1Controller"
prefHeight="400.0" prefWidth="600.0" spacing="10">
<children>
<VBox fx:id="vBoxScreen1" spacing="10" style="-fx-border-width:2px;-fx-border-color:red;-fx-background-color:yellow;" prefWidth="200" maxWidth="200" prefHeight="200">
<children>
<Button text="Button 1"/>
<Button text="Button 2"/>
<Button text="Button 3"/>
</children>
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</VBox>
<Button fx:id="transferButton" text="Transfer" onAction="#transferToScreen2"/>
</children>
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</VBox>
Screen2Controller.java
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
public class Screen2Controller {
#FXML
private VBox pane2;
#FXML
private HBox hBoxScreen2;
public void moveNode(Node node){
hBoxScreen2.getChildren().add(node);
}
}
screen2.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox fx:id="pane2" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.stackoverflow.javafx.issue7.Screen2Controller"
prefHeight="400.0" prefWidth="600.0" spacing="10">
<children>
<HBox fx:id="hBoxScreen2" spacing="10">
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</HBox>
</children>
<padding>
<Insets topRightBottomLeft="10" />
</padding>
</VBox>
I have a problem with my project in JavaFX. I use SceneBuilder which saves all changes to fxml file, but when i run project I see only empty background, even the size of the working area does not change. I try to clean project and workspace but problem is the same. I have refresh project and all files and nothing, I choose "Refresh using native hooks or pooling" but i have all time this same problem. Does anyone have any other idea? I use java 13 and JavaFX 11
mainWindow.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com /fxml/1" fx:controller="application.MainWindowController">
<children>
<Label fx:id="labelText" layoutX="286.0" layoutY="42.0" prefHeight="17.0" prefWidth="0.0" text="" />
<Button fx:id="button1" layoutX="270.0" layoutY="82.0" mnemonicParsing="false" text="OK" />
</children>
</Pane>
main.class
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass()
.getResource("mainWindow.fxml").toExternalForm());
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
MainWindowController class
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
public class MainWindowController implements Initializable {
#FXML
Label labelText;
#FXML
Button button1;
#Override
public void initialize(URL location, ResourceBundle resources) {
labelText.setText("Start");
}
}
i think the problem is in your start method in main. Try this implementation instead.
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
primaryStage.setTitle("Test");
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
This is what you expected?
i have tried all solution in youtube and google but none of them work. The problem is when i want to switch to new scene along sending data to another scene i got error saying
Null pointer Exception
public class FirstController implements Initializable {
#FXML private TextField textField;
#FXML private Button btn;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
public void onClick(ActionEvent event){
//switching to new scene
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/application/Second.fxml"));
try{
loader.load();
}catch(Exception e){ }
SecondController sn=loader.getController();
sn.setText(textField.getText());
Parent p=loader.getRoot();
Stage window=new Stage();
window.setScene(new Scene(p));
window.setTitle("dfd");
window.show();
}
}
main.fxml:
<AnchorPane prefHeight="450.0" prefWidth="650.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Main">
<children>
<Button fx:id="btn" layoutX="285.0" layoutY="264.0" mnemonicParsing="false" onAction="#onClick" text="Button" />
<TextField fx:id="textField" layoutX="237.0" layoutY="213.0" />
</children>
</AnchorPane>
Second controller :
public class SecondController implements Initializable {
#FXML private Label labelField;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
public void setText(String name){
this.labelField.setText(name);
}
}
Second.fxml:
`<AnchorPane prefHeight="450.0" prefWidth="650.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label fx:id="labelField" layoutX="297.0" layoutY="208.0" text="Label" />
</children>
</AnchorPane>
Your second FXML file is missing the fx:controller attribute. Therefore, when you load it, there is no controller, and so loader.getController() returns null.
Consequently, when you call sn.setText(...), you get a null pointer exception.
Just add the missing fx:controller attribute to the second FXML file. Assuming that SecondController is in the application package, this will look like:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Label?>
<AnchorPane prefHeight="450.0" prefWidth="650.0" fx:controller="application.SecondController" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label fx:id="labelField" layoutX="297.0" layoutY="208.0" text="Label" />
</children>
</AnchorPane>
Also note that it's very bad practice to silently "squash" exceptions, by using
try {
/* ... */
} catch (Exception e) { }
It's especially bad if the ensuing code depends on the code in the try block succeeding. In particular, in this case if your call to load() results in an exception (which may happen for a multitude of reasons, including the FXML not being found or being invalid, the fx:id and field names, or types,
not matching, method names for handlers not matching or having the wrong parameters, etc.), then the load method will silently fail and the controller will not be set in the loader. This will again result in sn being null.
Here's a complete example using your code, but with the other errors fixed as well as the missing fx:controller attribute inserted:
application/Main.java:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(FXMLLoader.load(getClass().getResource("First.fxml")));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
application/First.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<AnchorPane prefHeight="450.0" prefWidth="650.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.FirstController">
<children>
<Button fx:id="btn" layoutX="285.0" layoutY="264.0" mnemonicParsing="false" onAction="#onClick" text="Button" />
<TextField fx:id="textField" layoutX="237.0" layoutY="213.0" />
</children>
</AnchorPane>
application/FirstController.java:
package application;
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.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class FirstController implements Initializable {
#FXML private TextField textField;
#FXML private Button btn;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
public void onClick(ActionEvent event) throws Exception {
//switching to new scene
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/application/Second.fxml"));
loader.load();
SecondController sn=loader.getController();
sn.setText(textField.getText());
Parent p=loader.getRoot();
Stage window=new Stage();
window.setScene(new Scene(p));
window.setTitle("dfd");
window.show();
}
}
application/Second.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Label?>
<AnchorPane prefHeight="450.0" prefWidth="650.0" fx:controller="application.SecondController" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label fx:id="labelField" layoutX="297.0" layoutY="208.0" text="Label" />
</children>
</AnchorPane>
application/SecondController.java
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class SecondController implements Initializable {
#FXML private Label labelField;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
public void setText(String name){
this.labelField.setText(name);
}
}