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
Related
I have a little problem, actually I'm trying to display this url
https://s.ankama.com/www/static.ankama.com/wakfu/portal/game/item/115/13620723.png
in an ImageView but it doesn't work and I don't understand why.
I tried with this url https://gluonhq.com/wp-content/uploads/2015/11/phones-v3-small#2x.png
and it works perfectly fine.
Here's the code :
FXML part :
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<ImageView fitWidth="50" fitHeight="50">
<Image url="https://s.ankama.com/www/static.ankama.com/wakfu/portal/game/item/115/13620723.png"/>
</ImageView>
</GridPane>
Java part :
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 {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Note : I use JavaFX with JDK 8
I'm trying to set a JavaFx scene where a nested FlowPane can be changed during the runtime. I want to be able to new elements to the Pane, and have them rendered out.
Main
package renderable;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
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.FlowPane;
import javafx.stage.Stage;
public class Main extends Application implements EventHandler<ActionEvent{
Button button;
#FXML public FlowPane flowPane;
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("/renderable/ClientRender.fxml"));
Scene scene = new Scene(root, 1000, 720);
primaryStage.setTitle("The Game");
primaryStage.setScene(scene);
primaryStage.show();
loader.getController().updateFlowPanel();
primaryStage.show();
}
public void updateFlowPanel(){
flowPane.getChildren().add(button);
}
#Override
public void handle(ActionEvent event) {
if (event.getSource() == button)
System.out.println("The first button was pushed.");
}
}
RenderManager
public class RenderManager implements EventHandler<ActionEvent>{
cCard tableCards[];
cHand thisPlayerHand;
#FXML public FlowPane flowPane;
Button button;
public RenderManager() {
}
public void updateFlowPanel(){
flowPane.getChildren().add(button);
}
#Override
public void handle(ActionEvent event) {
if (event.getSource() == button)
System.out.println("The first button was pushed.");
}
}
ClientRender.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?package renderable?>
<?import javafx.geometry.*?>
<FlowPane id="flowPane" fx:id="flowPane" hgap="4.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="480.0" prefWidth="600.0" prefWrapLength="1400.0" style="-fx-background-color: #006600;" vgap="4.0" BorderPane.alignment="CENTER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="renderable.Main"/>
I am unable to correctly reference the RenderManager object associated with my current root to then run it's `updateFlowPanel()' method.
How do I correctly add elements to the Pane during runtime?
The reason you are getting a null pointer exception is that the #FXML-injected field flowPane is only initialized in the controller. You are calling updateFlowPanel() on an instance of Main that is not the controller, hence flowPane is null in that instance, and consequently flowPane.getChildren() throws a null pointer exception.
Specifically: Application.launch() creates an instance of the Application class and calls start() on that instance (among other things).
Calling FXMLLoader.load(), by default, creates an instance of the class specified by the fx:controller attribute in the FXML root element. Any #FXML-injected fields are initialized in that instance, event handler methods are called on that instance, and the initialize() method is called on that instance.
Since you use the same class Main for both the Application and the controller class - which is always a mistake, you end up with two instances of Main. You call updateFlowPanel() from start(), i.e. you call it on the instance created by Application.launch(); however flowPane is only initialized in the instance created by the FXMLLoader.
You should use a separate controller class. I.e. do
package renderable;
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("/renderable/ClientRender.fxml"));
Scene scene = new Scene(root, 1000, 720);
primaryStage.setTitle("The Game");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.show();
}
}
and then define a controller class:
package renderable ;
import javafx.fxml.FXML ;
import javafx.scene.layout.FlowPane ;
import javafx.scene.control.Button ;
public class Controller {
#FXML
private FlowPane flowPane ;
public void updateFlowPanel(){
Button button = new Button("...");
flowPane.getChildren().add(button);
}
}
and update the FXML to refer to it:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?package renderable?>
<?import javafx.geometry.*?>
<FlowPane id="flowPane" fx:id="flowPane" hgap="4.0"
nodeOrientation="LEFT_TO_RIGHT" prefHeight="480.0" prefWidth="600.0"
prefWrapLength="1400.0" style="-fx-background-color: #006600;" vgap="4.0"
BorderPane.alignment="CENTER" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="renderable.Controller"/>
Now you can either just call updateFlowPanel() in the initialize() method:
package renderable ;
import javafx.fxml.FXML ;
import javafx.scene.layout.FlowPane ;
import javafx.scene.control.Button ;
public class Controller {
#FXML
private FlowPane flowPane ;
public void initialize() {
updateFlowPanel();
}
public void updateFlowPanel(){
Button button = new Button("...");
flowPane.getChildren().add(button);
}
}
or you can call it on the correct controller instance from your start() method:
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/renderable/ClientRender.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
controller.updateFlowPanel();
Scene scene = new Scene(root, 1000, 720);
primaryStage.setTitle("The Game");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.show();
}
Edit:
The update you made to your question actually prevents the code from compiling. If you fix the compile error by replacing
loader.getController().updateFlowPanel();
with
loader.<Main>getController().updateFlowPanel();
then the (entirely different) issue you have created is that you create an instance of FXMLLoader:
FXMLLoader loader = new FXMLLoader();
but then instead of using that FXMLLoader instance to load the FXML, you call the static method load(URL):
Parent root = loader.load(getClass().getResource("/renderable/ClientRender.fxml"));
Since you are calling the static method, you never call load() on the FXMLLoader instance, and so its controller property is never initialized. Consequently, loader.<Main>getController() is null, and loader.<Main>getController().updateFlowPanel() throws a null pointer exception (entirely different to the null pointer exception you had in the previous version of your question; it is not even thrown from the same method).
If you want to reference the controller, use the code in the second version of the start() method that I posted above.
I want to create new label in another fxml when a button is clicked. I used the following code but the following exception is thrown
java.lang.RuntimeException:
java.lang.reflect.InvocationTargetException
The code:
Controller.java
public class Controller {
public AnchorPane anchorPane;
public void OpenSecond() throws Exception{
Stage primaryStage = new Stage();
Parent root = FXMLLoader.load(getClass().getResource("sample1.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public void AddLabe(){
Label label = new Label("Label");
anchorPane.getChildren().add(label);
}
}
second.java
public class Second {
public void AddLiabel()
{
Controller controller = new Controller();
controller.AddLabe();
}
}
I am using IntelliJ IDEA and Scene builder
You should be getting this exception because Java couldn't find the specified file or it is empty. Search for
Caused by: javafx.fxml.LoadException:
in the exception.
Adding the "sample1.fxml" and initializing it with a pane should fix the problem.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="275.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" />
Most probably you miss annotation for anchorPane and JavaFX didn't initialize it. Try next:
#FXML
public AnchorPane anchorPane;
and make sure anchorPane is listed in your FXML file.
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 a JavaFX Application. I have a button that when clicked calls an action and loads another fxml file. This works perfectly.
I decided to place this functionality from within the menu of the application.
So, I created a Menu and I added Menu Items from within the Scene builder. I properly assign the 'On Action' event, the same way I did with my other button. However I get the following error when I click:
Glass detected outstanding Java exception at -[GlassViewDelegate sendJavaMouseEvent:]:src/com/sun/mat/ui/GlassViewDelegate.m:541
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
Caused by: java.lang.ClassCastException: javafx.scene.control.MenuItem cannot be cast to javafx.scene.Node
at plataformavalidezpredictiva.MainController.handleAction(MainController.java:60)
... 38 more
This is the code for the handler. Once again, this works for a button I placed within the UI and doesn't work from the menu bar:
public void handleAction(ActionEvent event) throws Exception{
Node node = (Node)event.getSource();
Stage stage=(Stage) node.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml/GUIFile.fxml"));
Parent root = (Parent)fxmlLoader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
The line that seems to give me the problem is:
Node node = (Node)event.getSource();
Any ideas?
Edit: I saw the post here Unable to get Scene from MenuItem in JavaFX but that didn't work for me, because it didn't find the getScene() method for the menu bar.
Inject some node (e.g. the MenuBar, but it really can be any node in the same scene) into the controller. Call getScene() on that node and getWindow() on the Scene.
E.g.
Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuItem?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="exitfrommenu.MainController">
<top>
<MenuBar fx:id="menuBar">
<Menu text="File">
<MenuItem text="Exit" onAction="#exit"/>
</Menu>
</MenuBar>
</top>
</BorderPane>
MainController.java
package exitfrommenu;
import javafx.fxml.FXML;
import javafx.scene.control.MenuBar;
public class MainController {
#FXML
private MenuBar menuBar ;
#FXML
private void exit() {
Stage stage = (Stage) menuBar.getScene().getWindow() ;
// This exits the application, but of course you can do anything
// you like with the stage, such as showing a new scene in it:
stage.hide();
}
}
Main.java
package exitfrommenu;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}