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.
Related
I made a custom control CustomControl in JavaFX.
The CustomControl is basically just a button and a text field. It is made of a CustomControl.fxml and a controller CustomControl.java.
I also have a view TestView.fxml which includes the CustomControl with a controller TestController.java. The TestView.fxml has a button. When that button is pressed it calls the method 'OnButtonPress' from the TestController.java. Now in that method I would like to call the method setText from the controller CustomControl.java.
Now I'm facing two problems.
First: I'm getting the exception 'Root hasn't been set. Use method setRoot() before load.'
Second: I don't know how to call the method setText from my custom control.
Here's the code:
TestView.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:controller="controller.TestController" 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">
<children>
<fx:include source="../custom_control.fxml" fx:id="custom_control"/>
<Button onAction="#OnButtonPress" fx:id="button" layoutX="148.0" layoutY="117.0" mnemonicParsing="false" text="Button" />
</children>
</AnchorPane>
TestController.java:
package controller;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
public class TestController {
#FXML
private Button button;
#FXML
private VBox custom_control;
public TestController() {
}
#FXML
private void OnButtonPress() {
// that is the method I would like to call
custom_control.setText("This is the new text");
}
}
CustomControl.java:
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
public class CustomControl extends VBox {
#FXML private TextField textField;
public CustomControl() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("custom_control.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void setText(String text){
textField.textProperty().set(text);
}
#FXML
protected void doSomething() {
textField.textProperty().set("The button was clicked");
}
}
CustomControl.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml">
<TextField fx:id="textField"/>
<Button text="Click Me" onAction="#doSomething"/>
</fx:root>
Okey so I figured out why I didn't work so I though I'd share what problems I had.
the name's of the packages have to start with a lower case letter not upper case
The names of the Classes as well as the views (FXML files) have to start with a upper case letter.
instead of <fx:include> you should use the actual tag of the control (in my case <CustomControl>) and import the FXML file
When doing all that it finally worked.
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 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);
}
}
I need to manipulate an element from the Controller class. However, every example I’ve managed to find does this manipulation through the class that extends Application.
I need to manipulate the TextField inputText from VBoxSampleController in the class Main.
The manipulation is as follows:
inputText.setText(“TEST”);
The desired result is once the FXML application is launched, the inputText TextField is pre-populated with the text “TEST”.
My confusion stems from how to manipulate this field anywhere but:
VBoxSampleController
UIManager
The act of setting VBoxsampleController static so that I can access it by using the class name:
UIManager.vbsc.inputText.setText("TEST");
Does nothing. The element in the launched FXML window does not change. It remains empty.
This project is taken from https://examples.javacodegeeks.com/desktop-java/javafx/javafx-applications-efxclipse/ because I can’t create a new FXML application at this time.
Thanks.
FULL CODE:
/* Main */
public class Main {
public static void main(String[] args) {
UIManager.main(args);
// Does nothing
// UIManager.vbsc.inputText.setText("TEST");
// I want to change inputText here
}
}
/* UIManager */
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.fxml.FXMLLoader;
public class UIManager extends Application {
public static VBoxSampleController vbsc;
public FXMLLoader loader;
#Override
public void start(Stage primaryStage) {
try {
loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("VBoxSample.fxml"));
VBox root = loader.load();
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
vbsc = loader.getController();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
/* VBoxController */
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
public class VBoxSampleController {
#FXML
// The reference of inputText will be injected by the FXML loader
public TextField inputText;
// The reference of outputText will be injected by the FXML loader
#FXML
private TextArea outputText;
// location and resources will be automatically injected by the FXML loader
#FXML
private URL location;
#FXML
private TabPane mainPane;
#FXML
private ResourceBundle resources;
// Add a public no-args constructor
public VBoxSampleController() {
}
#FXML
private void initialize() {
}
#FXML
private void printOutput() {
outputText.setText(inputText.getText());
}
}
<!-- FXML -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="VBoxSampleController">
<children>
<Label alignment="CENTER_LEFT" cache="true" cacheHint="SCALE" prefHeight="30.0" prefWidth="200.0" text="Please insert Your Input here:" textAlignment="LEFT" />
<TextField fx:id="inputText" prefWidth="100.0" />
<Button alignment="CENTER_RIGHT" contentDisplay="CENTER" mnemonicParsing="false" onAction="#printOutput" text="OK" textAlignment="CENTER" />
<Label alignment="CENTER_LEFT" cache="true" cacheHint="SCALE" prefHeight="30.0" prefWidth="200.0" text="Your Input:" textAlignment="LEFT" />
<TextArea fx:id="outputText" prefHeight="100.0" prefWidth="200.0" wrapText="true" />
</children>
</VBox>
I think this happens because you're trying to modify a static variable from a non-static method:
#Override
public void start (Stage primaryStage) { // non-static method
vbsc = loader.getController(); // vbsc is a static variable
}
If I'm guessing right, you're trying to dynamically update an FXML field when the application starts. So you can simply achieve this by using controller's initialize() method:
public class VBoxSampleController {
#FXML // I suggest you to always keep class variable
private TextField inputText; // field private or non-public to prevent
// unauthorized modifications
...
#FXML
public void initialize() {
String text = "TEST"; // or any other at-runtime generated string
inputText.setText(text);
}
...
}
public class Main extends Application {
//controllers
static HomeController homeController;
static StageController stageController;
//...
}
In the initialize methode of your HomeControllers class write :
homeController = this;
and in the initialize methode of your StageController class write :
StageController = this;
Your problem appears to stem from the fact you're doing your update on the main thread and not on the platform thread. There are two problems here.
You should always make ui changes on the platform thread.
You are executing the UIManager.vbsc.inputText.setText("TEST"); before the launch method.
Try.
Platform.runLater( () -> UIManager.vbsc.inputText.setText("TEST") );
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;