This question already has an answer here:
javafx 8 compatibility issues - FXML static fields
(1 answer)
Closed 5 years ago.
I am new to JavaFX. Trying to follow a tutorial to generate a scene and associate eventhandler with a button.
I created a Main.FXML and edited in SceneBuilder. Since I added SceneBuilder's path in my IDE, it is able to detect my main controller. I wrote a function to generate random number.
public class MainController {
public static void generateRandom(ActionEvent event) {
Random rand = new Random();
int myrand = rand.nextInt(500) + 1;
System.out.print(Integer.toString(myrand));
}
}
In scenebuilder, it detects this method in controller which can be easily added as eventhandler for OnAction of the button. Main.FXML is updated after the operations.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="300" prefWidth="500" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111" fx:controller="application.MainController">
<children>
<Button layoutX="204.0" layoutY="204.0" mnemonicParsing="false" onAction="#generateRandom" text="Button" />
<Label layoutX="138.0" layoutY="36.0" prefHeight="144.0" prefWidth="210.0" />
</children>
</AnchorPane>
My main application class is as below:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/application/Main.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("My Title");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When I run the app, I got error:
javafx.fxml.LoadException: Error resolving onAction='#generateRandom',
either the event handler is not in the Namespace or there is an error
in the script.
/C:/Users/Oscar/Workspace/Sunoco/bin/application/Main.fxml:10
Error resolving “onAction” while loading FXML suggests it may have to do with wrong import for ActionEvent which is not the case. Plus everything is set up automatically using SceneBuilder and Eclipse. So why am I getting this error?
I think the problem will be solved if you use an #FXML annotation on the eventMethod. Besides, try not to make methods static in the controller, as it may be confusing/misleading. If you need that method to be accesible from elsewhere, consider declaring it in a separate (utility like) class.
Related
I am attempting to create a method in my main screen controller to set a new scene in my program. When attempting to do so, I get the following error:
Error resolving onAction='#partAddButtonPushed', either the event handler is not in the Namespace or there is an error in the script.
The relevant fxml code is:
<AnchorPane id="AnchorPane" prefHeight="420.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="john.Doe.View_Controller.MainController">
<children>
<Button id="addPartsButton" fx:id="partAdd" layoutX="190.0" layoutY="340.0" maxWidth="70.0" minWidth="70.0" mnemonicParsing="false" onAction="#partAddButtonPushed" prefWidth="70.0" text="Add">
and the relevant code in the controller is:
public class MainController implements Initializable {
#FXML
private Button partAdd;
#FXML
private void partAddButtonPushed(ActionEvent event) throws IOException {
Parent partAddParent = FXMLLoader.load(getClass().getResource("/View/AddPart.fxml"));
Scene AddPartScene = new Scene (partAddParent);
Stage window = (Stage)((Node)event.getSource()).getScene().getWindow();
window.setScene(AddPartScene);
window.show();
}
Would anyone happen to know why they are not linking up?
----Revised draft----
I've been trying to create a game using JavaFX. In the game, a player sees a menu page at first("play game" ,"options","exit", etc), and he/she clicks the "OnlineGame" button to start a game.
And now I've already had a menu (created with fxml) and its controller.
<!-- MainMenu.fxml -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.media.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.effect.*?>
<?import java.lang.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainMenuController">
<children>
<Button fx:id="OnlineGame" layoutX="55.0" layoutY="157.0" mnemonicParsing="false" onAction="#OnlineGame" opacity="0.6" prefHeight="51.0" prefWidth="229.0" text="Play Online Game" textFill="#2067a1">
<font>
<Font size="21.0" />
</font>
</Button>
</children>
</AnchorPane>
The controller:
import *****;
//MainMenuController.java
public class MainMenuController{
#FXML private Button OnlineGame;
#FXML protected void OnlineGame(ActionEvent event) throws IOException{
//start the game
}
}
The menu is initialized in Main.start and it works well.
import *****;
public class Main extends Application {
public void start(Stage Stage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("MainMenu.fxml"));
Scene scene = new Scene(root,730,439); //set SceneSize
Stage.setScene(scene);
Stage.show();
//call three methods to start a game
prepareGame();
addHandlers(scene);
startGame();
} catch(Exception e) {
e.printStackTrace();
}
}
private void prepareGame(){**********}
private void addHandlers(Scene scene){***********}
private void startGame(){****}
public static void main(String[] args) {
launch(args);
}
However, I also have some methods in Main.start to initialize a game.(prepareGame(); addHandlers(scene); startGame();). As you see, I also need to put them after stage.show;. So now the problem is that when I run my program, those three methods runs immediately (before I click my "OnlineGame" button in the menu!).
So are there any ways to achieve my goal(i.e. a player sees the game page after clicking "OnlineGame")?
PS: I simplified my code to make my question short(although it's still long :-D). I'm not a native speaker so if my words are confusing, please let me know so that I can explain it again. What's more, I'd like to post more details if necessary.
Thanks in advance!
Move all that logic to the controller: it has no place in the Application class.
If you want those three method to only be invoked when the button is pressed, then the obvious thing to do is to invoke them from the button's handler method:
public class MainMenuController {
#FXML
private Button onlineGame ;
#FXML
private void onlineGame(ActionEvent event) {
// button can't be pressed until it is in a scene, so getScene()
// here cannot return null:
addHandlers(game.getScene());
prepareGame();
startGame();
}
// ...
}
(Note that I changed the field and some method names so that they conform to standard Java naming conventions. You will need to change the FXML file to match these names.)
So I'm trying to move from Swing to JavaFX and I just declare the start() and launch() methods without knowing how they work. But below code prints false and false to the console. However when I click the button in the GUI, built with Scene Builder, that executes myMethod(), this time it prints true. Why does it say that primaryStage isn't instantiated?
addition information:
I also made this class my Controller, for the same reason - it needs access to the Stage reference. The full version of Main, which I didn't post, implements Initializable if that matters.
As a bonus question I was wondering if I need the field of primaryStage to reference the Application Stage, which there would only be one of?, in myMethod().
public class Main extends Application {
private Stage primaryStage;
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
try {
Scene scene = new Scene(FXMLLoader.load(getClass().getResource("Sample.fxml")),600,400);
primaryStage.setScene(scene);
} catch(Exception e) {
e.printStackTrace();
}
primaryStage.show();
//both lines below print false; As they should.
System.out.println(this.primaryStage == null);
myMethod();
}
public static void main(String[] args) {
launch(args);
}
public void myMethod() {
System.out.println(primaryStage == null);
}
}
EDIT
placing this FXML document in the same folder as above class will let you run Main to see that the button does indeed print true.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<HBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Main">
<children>
<Button mnemonicParsing="false" onAction="#myMethod" text="Button" />
</children>
</HBox>
That is because the FXMLLoader will create new instance of application.Main class, in where the start() method is not invoked and thus private Stage primaryStage is null.
It is better to separate the main class and the controller for the FXML, and pass the primary stage later if it is necessary:
...
try
{
FXMLLoader loader = new FXMLLoader( getClass().getResource( "Sample.fxml" ) );
Scene scene = new Scene( loader.load(), 600, 400 );
( (MyController) loader.getController() ).setPrimaryStage(primaryStage);
primaryStage.setScene( scene );
}
catch ( Exception e )
{
e.printStackTrace();
}
...
where the MyController class can be as simple as:
public class MyController {
private Stage primaryStage;
public void setPrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage;
}
}
but also can implement Initializable interface. Refer to Introduction to FXML :: Controllers.
Also note that you can get the scene and its stage from any node that is part of that constructed scene graph (i.e. a stage that is shown) by:
Scene scene = anynode.getScene();
Stage primaryStage = (Stage) anynode.getScene().getWindow();
Of course, for the secondary stages created by you the getWindow() will return that stage and not the primary stage.
There is no need at all to jump through any of these hoops to get access to the window. You can just call getScene().getWindow() on any node to get the window in which it is displayed (and of course you can just inject any node into your controller in the usual way).
Don't use the Application subclass as the controller class: you will have (at least) two different instances (one created by the launch() method, because it's the Application subclass, and one created by the FXMLLoader, because it's the controller class). Different fields will be initialized in the different instances.
Create a controller class, and inject at least one node into it:
public class Controller {
#FXML
private Parent root ;
#FXML
private void myMethod() {
Window window = root.getScene().getStage();
// assuming you are running as a standalone application, the window
// will actually be a Stage instance.
window.hide(); // for example...
}
}
Use this as your controller class and inject the node into it:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<HBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller" fx:id="root">
<children>
<Button mnemonicParsing="false" onAction="#myMethod" text="Button" />
</children>
</HBox>
I have made a basic login screen in netbeans using javafx and scenebuilder 2.0! by copying a youtube tutorial.
<Button id="btnLogin" layoutX="146.0" layoutY="243.0" mnemonicParsing="false" onAction="#initialize" prefHeight="59.0" prefWidth="76.0" text="Login" />
as shown above, netbeans underline "#initialize" and give the folowing error "Handler method is not accessible. Make public, or annotate with #FXML"
and the program doesn't want to start up or even show me my stage.
LoginController:
#FXML private void initialize (ActionEvent event)
{
System.out.println("test");
}
if i simply delete the code "onAction="#initialize" my program runs and shows me an interface, but my button to login doesn't work(obviously)
P.S. making it public doesnt work
any advice will appreciated thanks a lot in advance
code:
Main:
public class Login extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Login.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Login");
stage.show();
}
Controller:
public class LoginController implements Initializable {
#FXML
private Label lblmessage;
#FXML
private TextField txtUsername;
#FXML
private Button btnLogin;
#FXML
private PasswordField txtPassword;
#FXML private void handlebutton()
{
System.out.println("test");
}
/**
* Initializes the controller class.
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
I guess the method name is the problem.
The method #FMXL protected void initialize() may be used as an additional callback on startup.
You can use the initialize method e.g. to dynamically fill your UI.
So:
Try to rename the method in code and fxml file.
As given here:
An instance of the FXMLLoader class simply looks for the initialize()
method on the controller and calls it, if available. Note that, similar to other FXML callback methods such as event handlers, this method must be annotated with the #FXML annotation if it is not public.
I have just rebuilt your code. I also added this main method to the Login:
public static void main(String[] args){
launch(args);
}
This is the Login.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="LoginController">
<children>
<Label fx:id="lblmessage" text="Label" />
<TextField fx:id="txtUsername" />
<Button fx:id="btnLogin" mnemonicParsing="false" onAction="#handlebutton" text="Button" />
<PasswordField fx:id="txtPassword" />
</children>
</VBox>
And with this setup, everything works as expected.
The Stage is loaded, the handlebutton method is called when I click the button and the initialize method is called on startup.
The error currently seems to be not reproducable.
thanks all! my issue was that in my fxml file i was naming my textfields/password fields fields
<TextField id="txtUsername"....... etc
<Label id="lblmessage" ...... etc
<PasswordField id="txtPassword".... etc
therefore i received a null pointed everytime a null pointer everytime i tried to access those variable from my controller/main class, because my program wasnt recieving any data because i forgot my fX:id, so the above should have looked like the following
<TextField fx:id="txtUsername"....... etc
<Label fx:id="lblmessage" ...... etc
<PasswordField fx:id="txtPassword".... etc
then my program worked perfectly
thanks everyone for their help
I have a small code of javaFX.
I want to draw in canvas but I have some problem of beginner I really don't understand well javaFX.
This is my Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
Controller controller;
#Override
public void start(Stage primaryStage) throws Exception{
controller= new Controller();
controller.Pane = FXMLLoader.load(getClass().getResource("sample.fxml"));
System.out.printf("kk"+controller.canvas.isResizable());
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(controller.Pane, controller.Pane.getPrefWidth(), controller.Pane.getPrefHeight()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
this is sample.FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="Pane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="284.0" prefWidth="484.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<Canvas fx:id="canvas" height="267.0" layoutX="13.0" layoutY="9.0" width="459.0" AnchorPane.bottomAnchor="8.0" AnchorPane.leftAnchor="13.0" AnchorPane.rightAnchor="12.0" AnchorPane.topAnchor="9.0" />
</children>
</AnchorPane>
For the controller.java there is only canvas and pane declaration.
My question :
Pane is the parent and canvas is the children after doing FXMLloader.load etc, pane is not null but the children are null in the method start (outside like an event of button the children aren't null) why is children null?
To make the canvas resizable there is no method to do such thing, the only solution is to extends from Canvas and #Override method isResizable make it true. But Canvas I get it from FXML if I do
Canvas MyCanvas= new CanvasResizable();
this dosen't work I don't know why.
is there a way like android to make for example canvas=(Canvas) findbyid(FXML.id.canvas); (something similar)
and my last question how can we change the color of canvas there is no method to do that.
I'm sorry if my question is already asked by a diffirent why, I searched so much in google there is a lack of documentation of javaFX.
FXML controller should not be created directly. It will be initialized for you by FXMLLoader:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Pane root = loader.load(); // controller was initialized
controller = loader.getController();
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, root.getPrefWidth(), root.getPrefHeight()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can find more details in Oracle tutorials: http://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm#CACFEHBI