I am practicing a small chat box window and I try to make it so the user must enter a name into the textfield box (a username) but I been having problems and I am sure this will not be the first time. I keep getting nullpointerexception every time I try to use one of the variables, for right now, it is textfield. So I initialize it but then it is always blank string even when I type in a username.
app.java
public class app extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLApp.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
FXMLApp.fxml (deleted what was not needed)
<?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" maxHeight="900.0" maxWidth="800.0" minHeight="300.0" minWidth="200.0" prefHeight="600.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="chatappclient.FXMLChatAppClientController">
<children>
<TextField fx:id="username" layoutX="176.0" layoutY="488.0" promptText="enter username" />
<Button fx:id="login" layoutX="176.0" layoutY="519.0" mnemonicParsing="false" onAction="#login" text="Login" />
</children>
</AnchorPane>
as you can see the login button calls
onAction="#login"
last file: FXMLAppController
package chatappclient;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javax.swing.JOptionPane;
public class FXMLAppController implements Initializable
{
#FXML
private Pane top_container;
private Pane bottom_container;
private TextField username ;
private String name;
private Button login;
public FXMLChatAppClientController ()
{
// without this, I get nullpointerexception
// with it, the textfield is always null
username = new TextField();
}
#Override
public void initialize(URL url, ResourceBundle rb)
{}
#FXML
private void login()
{
// this returns empty string, if I did not initialize it, it would be an exception
System.out.println("DEBUG: " + username.getText());
}
}
You need to annotate each of the fields defined in the FXML file with #FXML:
private Pane top_container;
private Pane bottom_container;
#FXML
private TextField username ;
private String name;
#FXML
private Button login;
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 have a simple two tab application build around JavaFXML and scenebuilder. The tabs do nothing at present because I cannot get past a nullpointer exception when trying to load them.
The java and fxml files are arranged in a Netbeans project like this:
Main Application: The application main class MainApp.java sets the scene and is declared as follows:
package tabpane;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class MainApp extends Application {
private static Stage primaryStage;
private AnchorPane rootLayout;
public MainApp() {}
#Override
public void start(Stage primaryStage) {
primaryStage.initStyle(StageStyle.UNDECORATED);
MainApp.primaryStage = primaryStage;
MainApp.primaryStage.setTitle("Account Names");
initRootLayout();
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/MainView.fxml"));
rootLayout = (AnchorPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, e);
System.out.println("MainApp: IOException in method: initRootLayout" + e.getMessage());
System.exit(0);
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
The MainController class declares the two tabs, the Input tab and Account tab, for the application and an exit button for closing the program. This is the MainView. fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.MainController">
<children>
<TabPane fx:id="tabPane" prefHeight="345.0" prefWidth="600.0" style="-fx-background-color: blue;" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab fx:id="inputTab" closable="false" text="Input">
<content>
<fx:include source="InputView.fxml" />
</content>
</Tab>
<Tab fx:id="detailTab" closable="false" text="Detail">
<content>
<fx:include source="DetailView.fxml" />
</content>
</Tab>
</tabs>
</TabPane>
<Button fx:id="exitBtn" layoutX="529.0" layoutY="355.0" mnemonicParsing="false" onAction="#handleExitBtn" text="Exit" />
</children>
</AnchorPane>
This is the java controller class for the main view contained in MainController.java:
package tabpane1.view;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class MainController implements Initializable {
#FXML public TabPane tabPane;
#FXML public Tab inputTab;
#FXML public Tab accountTab;
#FXML private Button exitBtn;
public DetailController detailController;
public InputController inputController;
#Override
public void initialize(URL location, ResourceBundle resources) {
detailController = new FXMLLoader(getClass().getResource("DetailView.fxml")).getController();
// System.out.println(detailController.getClass());
inputController = new FXMLLoader(getClass().getResource("InputView.fxml")).getController();
// System.out.println(inputController.getClass());
}
#FXML private void handleExitBtn() {
System.exit(0);
}
}
The two println statements are simply trying to get the references to the two tabs, which are detailController and inputController, to do something. However, when these are uncommented and run, the following dump is output:
MainApp: IOException in method: initRootLayout
SEVERE: null
file:/G:/J2EE/TabPane_1/dist/run1314937364/TabPane_1.jar!/tabpane1/view/MainView.fxml
javafx.fxml.LoadException:
file:/G:/J2EE/TabPane_1/dist/run1314937364/TabPane_1.jar!/tabpane1/view/MainView.fxml
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
at tabpane1.MainApp.initRootLayout(MainApp.java:43)
at tabpane1.MainApp.start(MainApp.java:33)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(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$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at tabpane1.view.MainController.initialize(MainController.java:24)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
... 13 more
When the printlns are commented out, the outcome is as expected, that is the tab pane is correctly displayed:
As mentioned the detail and input controllers are empty, but for completeness they are as follows:
InputView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.InputController">
</AnchorPane>
The controller InputController.java:
package tabpane1.view;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
public class InputController implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
DetailView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: transparent;" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.DetailController" />
The controller DetailController.java:
package tabpane1.view;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
public class InputController implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
The way to get reference to the controller of an embedded FXML is described in this article.
You simply have to give an fx:id to the included resource:
<fx:include fx:id="IncludedView" source="InputView.fxml" />
Get a reference to IncludedView in the MainController:
#FXML private Parent IncludedView;
Get a reference to its controller simply by appending the word Controller in addition to the variable name of the embedded element:
#FXML private InputController IncludedViewController;
That is all.
Test it with:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
public class MainController implements Initializable {
#FXML private Parent IncludedView;
#FXML private InputController IncludedViewController; // $IncludedView;+Controller
#Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println(IncludedView.getClass());
System.out.println(IncludedViewController.getClass() );
}
#FXML private void handleExitBtn() {
System.exit(0);
}
}
An MRE for the sake of future readers:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class GetEmbeddedController extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane root = (Pane) FXMLLoader.load(getClass().getResource("MainView.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(null);
}
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
<children>
<fx:include fx:id="includedView" source="IncludedView.fxml" />
<Button layoutX="401.0" layoutY="354.0" onAction="#handleTestBtn" text="Test Included Controller" />
</children>
</AnchorPane>
MainController.java
import javafx.fxml.FXML;
import javafx.scene.Parent;
public class MainController{
#FXML private Parent includedView; //not used in this demo
/*
* Get a reference to IncludedView controller simply by appending
* the word Controller in addition to the variable name of the embedded
* element:$IncludedView;+Controller
*/
#FXML private IncludedViewController includedViewController;
#FXML private void handleTestBtn() {
includedViewController.showAlert();
}
}
IncludedView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Text?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="IncludedViewController">
<children>
<Text layoutX="277.0" layoutY="196.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Included Fxml" />
</children>
</AnchorPane>
IncludedViewController.java
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
public class IncludedViewController{
void showAlert(){
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("Alert invoked by IncludedViewController ");
alert.show();
}
}
I am trying to use multiple fxml files in an application I am making, and in doing some research, I found that using Custom Controllers for the fxml files is the best approach towards doing this type of application.
I followed an Oracle Docs tutorial on "Mastering FXML" and set the root and controller as "this" in the CustomController.java file.
The problem arises when intellij discovers there is no controller specified in the fxml file for the onAction handler, while I am specifying the controller programmatically.
tester.java
package task01;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class tester extends Application
{
#Override
public void start(Stage stage) throws Exception
{
CustomController customController = new CustomController();
customController.getStylesheets().add("/task01/stylize.css");
stage.setScene(new Scene(customController,1920,1080));
stage.setTitle("Seneca ATM Program");
stage.setWidth(1920);
stage.setHeight(1080);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
CustomContoller.java
package task01;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.fxml.*;
import javafx.scene.layout.Pane;
import java.io.IOException;
public class CustomController extends GridPane
{
#FXML
private Pane viewableContent;
#FXML
private Button vigilanteButton;
public CustomController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("root.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try
{
fxmlLoader.load();
} catch (IOException exception)
{
throw new RuntimeException(exception);
}
}
#FXML
private void vigilanteAction(ActionEvent actionEvent)
{
System.out.println("Hello, World");
}
}
root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.GridPane?>
<?import task01.MainMenuController?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>
<fx:root type="javafx.scene.layout.GridPane" xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
<ImageView fitWidth="229.67" fitHeight="149.67" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.halignment="CENTER">
<Image url="/task01/logo.png"/>
</ImageView>
<Pane fx:id="viewableContent" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.halignment="CENTER">
<MainMenuController/>
</Pane>
<Button fx:id="vigilanteButton">Vigilante</Button>
</fx:root>
MainMenuController.java
package task01;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
public class MainMenuController extends GridPane
{
private CustomController customController = new CustomController();
public MainMenuController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("mainmenu.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try
{
fxmlLoader.load();
} catch (IOException exception)
{
throw new RuntimeException(exception);
}
}
#FXML
private VBox buttonSet;
#FXML
private HBox buttonSetOne;
#FXML
private HBox buttonSetTwo;
#FXML
private Button changePinButton;
#FXML
private Button accountInquiryButton;
#FXML
private Button withdrawMoneyButton;
#FXML
private Button depositMoneyButton;
#FXML
private Button balanceInquiryButton;
#FXML
private Button createAccountButton;
#FXML
private GridPane gridpane;
#FXML
public void createAccountAction(ActionEvent actionEvent)
{
}
}
mainmenu.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<GridPane fx:id="gridpane" alignment="CENTER" vgap="50" hgap="50" xmlns:fx="http://javafx.com/fxml">
<padding><Insets top="10" bottom="10" left="10" right="10"/></padding>
<VBox fx:id="buttonSet" spacing="25" GridPane.columnIndex="0" GridPane.rowIndex="1">
<HBox fx:id="buttonSetOne" spacing="25">
<Button styleClass="menuButton" fx:id="createAccountButton" onAction="#createAccountAction">Create account</Button>
<Button styleClass="menuButton" fx:id="changePinButton">Change PIN</Button>
<Button styleClass="menuButton" fx:id="accountInquiryButton">Account Inquiry</Button>
</HBox>
<HBox fx:id="buttonSetTwo" spacing="25">
<Button styleClass="menuButton" fx:id="withdrawMoneyButton">Withdraw Money</Button>
<Button styleClass="menuButton" fx:id="depositMoneyButton">Deposit Money</Button>
<Button styleClass="menuButton" fx:id="balanceInquiryButton">Balance Inquiry</Button>
</HBox>
</VBox>
</GridPane>
Here's the problem, you can bind a FXML file to a Controller from the controller, but when you do this the IDE doesn't know it until it's up and running. That's why the IDE causes you troubles. If you want to set the onAction handler you'll have to do it from the controller. You have to create a method like this and add the onAction listener to the button:
#FXML
public void initialize() {
createAccountButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// TODO Auto-generated method stub
createAccountAction(event);
}
});
}
Divide and conquer: break complex problems to smaller ones. Start with a simple setup like the following.
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.Pane;
import javafx.stage.Stage;
public class tester extends Application
{
#Override
public void start(Stage stage) throws Exception
{
CustomController customController = new CustomController();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("root.fxml"));
fxmlLoader.setController(customController);
Parent root = (Parent)fxmlLoader.load();
stage.setScene(new Scene(root,1920,1080));
stage.setTitle("Seneca ATM Program");
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
//controller should be just that. It is not a pane
class CustomController /* extends GridPane */
{
#FXML
private Pane viewableContent;
#FXML
private Button vigilanteButton;
#FXML
private void vigilanteAction(ActionEvent actionEvent)
{
System.out.println("Hello, World");
}
}
root.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.GridPane?>
<!-- ?import task01.MainMenuController?-->
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>
<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
<ImageView fitWidth="229.67" fitHeight="149.67" GridPane.columnIndex="0" GridPane.rowIndex="0"
GridPane.halignment="CENTER">
<Image url="task01/logo.png"/> <!-- todo: set correct path -->
</ImageView>
<Button fx:id="vigilanteButton" onAction="#vigilanteAction">Vigilante</Button>
</GridPane>
I hope this helps you to structure your application.
There's no way for the IDE to know about you setting the controller yourself somewhere in your java code. Autocompletion features and the like are off for this reason. The fact that the IDE shows these kind of "issues" is unfortunate in this case, but they could indicate an issue that can only be recognized at runtime.
In your case assuming you don't have any typos in your fxml, it's safe to ignore these warnings.
You could of course simply add the fx:controller attribute temporarily while editing the fxml and remove it when you're done.
Another way of dealing with this issue would be specifying the fx:controller attribute in the fxml and setting the controllerFactory property instead of the controller property for the loader. I don't really like this approach though:
<GridPane fx:id="gridpane" alignment="CENTER" vgap="50" hgap="50" xmlns:fx="http://javafx.com/fxml" fx:controller="task01.MainMenuController">
...
public MainMenuController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("mainmenu.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setControllerFactory(clazz -> {
if (!clazz.isInstance(this)) {
throw new IllegalArgumentException(String.format("class of controller (%s) not assignable to class specified in fxml (%s)", this.getClass().getName(), clazz.getName()));
}
return this;
});
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
If you don't want to check assignment compatibility between the controller type specified in the fxml and the type of the controller you specify, the controllerFactory could be simplified to clazz -> this.
If you want to use this approach for multiple classes I strongly recommend creating a class for the controller factory to avoid repetitions, assuming you want to do the check...
I'm trying to set up a simple example for my program using JavaFX.
What I want to have is a Controller with a main.fxml; then the main.fxml will have a TabbedPane as root, with 2 tabs (tab1.fxml and tab2.fxml), each with its controller (Tab1Controller and Tab2Controller).
Now this might be the prolem, yet I don't see why it would be a problem:
Tab1Controller and Tab2Controller both extend Controller; because they share various fields which are manipulated, for example a bottom status bar which needs constant updates.
So far I managed to have working all controllers and .fxml.
When I try to set up the text of the label in the Controller from one of the subclasses it just points to null, yet the label is initialised in the gui with its default text..
Compiles correctly and lblController seems to be correctly linked to its fx:id in main.fxml.
Any help/links are much appreciated.
I've been looking at various posts but the only one getting close to what I need it this one:
JavaFX 8, controllers inheritance
Main:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("../view/main.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 600, 700));
primaryStage.show();
}
}
Main controller:
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller {
#FXML
public Label lblController;
public Controller() {
System.out.println("CONTROLLER MAIN - CONSTRUCTOR");
}
#FXML
public void initialize() {
System.out.println("CONTROLLER MAIN - INITIALIZER");
}
protected void setSts(String sts) {
lblController.setText(sts);
}
}
main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.60"
fx:controller="sample.Controller">
<BorderPane>
<center>
<TabPane tabClosingPolicy="UNAVAILABLE">
<Tab text="Tab 1">
<AnchorPane>
<!--FOR fx:id HAS TO HAVE THE SAME NAME BUT IN LOWER CASE AS THE .fxml-->
<fx:include fx:id="tab1" source="tab/tab1.fxml"/>
</AnchorPane>
</Tab>
<Tab text="Tab 2">
<AnchorPane>
<fx:include fx:id="tab2" source="tab/tab2.fxml"/>
</AnchorPane>
</Tab>
</TabPane>
</center>
<bottom>
<AnchorPane>
<Label fx:id="lblController" text="Controller Test"/>
</AnchorPane>
</bottom>
</BorderPane>
</AnchorPane>
Tab1Controller
package sample.ctrTab;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import sample.Controller;
public class Tab1Controller extends Controller {
#FXML
Label lbl1;
#FXML
TextField txt1;
#FXML
Button btn1Save, btn1Load;
public Tab1Controller() {
super();
System.out.println("CONTROLLER TAB 1 - CONSTRUCTOR");
}
#FXML
void btn1Save() {
System.out.println("CONTROLLER TAB 1 - SAVE CLICK");
// lblController.setText("ANYTHING"); //NULL POINTER HERE
// setSts(txt1.getText()); //OR HERE
}
#FXML
void btn1Load() {
System.out.println("CONTROLLER TAB 1 - LOAD CLICK");
}
protected void setSts(String sts) {
super.setSts(sts);
}
}
tab1.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.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.60" fx:controller="sample.ctrTab.Tab1Controller">
<children>
<Label fx:id="lbl1" layoutX="100" layoutY="50" text="Def tab 1"/>
<TextField fx:id="txt1" layoutX="100" layoutY="70"/>
<Button fx:id="btn1Save" onAction="#btn1Save" layoutX="100" layoutY="140" mnemonicParsing="false"
text="Save text"/>
<Button fx:id="btn1Load" onAction="#btn1Load" layoutX="200.0" layoutY="140" mnemonicParsing="false"
text="Send to tab 2"/>
</children>
</AnchorPane>
The fields may be present in the controller classes, however FXMLLoader still creates new controller instances for every fxml loaded given these fxmls. Since tab1.fxml does not contain a Label tag with fx:id="lblController" as attribute, the lblController field is never injected to the Tab1Controller instance.
You would be better off not extending Controller, but passing a reference of it to the child controllers:
public class Controller {
#FXML
public Label lblController;
#FXML
private Tab1Controller tab1Controller;
#FXML
private Tab2Controller tab2Controller;
public Controller() {
System.out.println("CONTROLLER MAIN - CONSTRUCTOR");
}
#FXML
public void initialize() {
System.out.println("CONTROLLER MAIN - INITIALIZER");
tab1Controller.initParentController(this);
tab2Controller.initParentController(this);
}
public void setSts(String sts) {
lblController.setText(sts);
}
}
public class Tab1Controller {
#FXML
Label lbl1;
#FXML
TextField txt1;
#FXML
Button btn1Save, btn1Load;
public Tab1Controller() {
System.out.println("CONTROLLER TAB 1 - CONSTRUCTOR");
}
private Controller parentController;
public void initParentController(Controller controller) {
parentController = controller;
}
#FXML
void btn1Save() {
System.out.println("CONTROLLER TAB 1 - SAVE CLICK");
parentController.lblController.setText("ANYTHING");
parentController.setSts(txt1.getText());
}
#FXML
void btn1Load() {
System.out.println("CONTROLLER TAB 1 - LOAD CLICK");
}
}
Note that for the child controllers you could actually use a abstract superclass implement the initParentController method only once.
I have this controller:
package cz.vutbr.feec.bmds.cv2;
import java.awt.Button;
import java.awt.TextField;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Slider;
public class TestGuiController {
private int buttonPressed = 0;
#FXML
private Button tlacitko;
#FXML
private TextField textovePole;
#FXML
private Slider slider;
public void buttonPressed(ActionEvent e) {
buttonPressed++;
textovePole.setText(Integer.toString(buttonPressed));
}
}
this fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
<AnchorPane prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml" fx:controller="cz.vutbr.feec.bmds.cv2.TestGuiController">
<children>
<Button fx:id="tlacitko" layoutX="30.0" layoutY="40.0" mnemonicParsing="false" onTouchPressed="#buttonPressed" text="Button" />
<Slider fx:id="slider" layoutX="157.0" layoutY="17.0" orientation="VERTICAL" />
<TextField fx:id="textovePole" layoutX="14.0" layoutY="89.0" prefWidth="134.0" />
</children>
</AnchorPane>
and this is my main class:
package cz.vutbr.feec.bmds.cv2;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("TestGui.fxml"));
primaryStage.setTitle("Titulek");
primaryStage.setScene(new Scene(root,300,275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When I run this through ant I get message box with error (exception during running application). I tried simple fxml without controller and it works so I am guesing I do something wrong with controller. What I must change to have it working?
I must answer my own question. Problem was in TestGuiController where I used java.awt.Button and java.awt.TextField instead of javafx.scene.control.Button and javafx.scene.control.TextField.
I'm not 100% sure but try:
in FXML: Button: onAction instead of onTouchPressed
Please provide the exact Exception message.