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.
Related
So I am working on a javaFX application and I want to create multiple <ImageView> using for loop!
Is that possible to make for/foreach loop in a .fxml file ?
If yes , then how ?
Also i have another question! how to send data from the controller to sample.fxml file ?
for exemple i want to send a table form controller.java to sample.fxml file and use that table+for loop to make <ImageView> in the fxml file!
Note: I am using the fxml to display the image because I am using those images as buttons.
Here is the code of sample.fxml :
<?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 fitHeight="120" fitWidth="120" fx:id="panda" GridPane.columnIndex="0" GridPane.rowIndex="0" onMousePressed="#mousePressed">
<image>
<Image url="#pics/panda.png">
</Image>
</image>
</ImageView>
</GridPane>
Here is the code of Controller.java :
package sample;
import javafx.scene.input.MouseEvent;
import javafx.scene.media.AudioClip;
public class Controller {
public void play_audio()
{
AudioClip sound = new AudioClip(this.getClass().getResource("voices/panda.mp3").toString());
sound.play();
}
public void mousePressed() {
play_audio();
}
}
Code of Main.java :
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
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("Animal Sound");
Scene scene = new Scene(root, 790, 675);
scene.getStylesheets().add("sample/styles.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I don't think flow control is manageable in an fxml file. Also you don't really pass arguments to an fxml file.
I think you've separated this well, and a little more paring down should get it to work. I would recommend to specify the sounds and the images from the controller. So I would make the controller for each button.
package sample;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
public class AudioButtonController {
String sound;
#FXML
ImageView image;
public void setAudioLocation(String resourcePath){
sound = resourcePath;
}
public void setImageLocation(String img){
image.setImage( new Image( img ) );
}
public void mousePressed() {
System.out.println(sound);
}
}
Now your fxml for each button can be.
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns:fx="http://javafx.com/fxml" fx:controller="sample.AudioButtonController">
<ImageView
fitHeight="120" fitWidth="120" fx:id="image" onMousePressed="#mousePressed">
</ImageView>
</HBox>
Here is an example main class that starts up and loads 2 audio buttons, but it could be used to load N buttons.
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application{
#Override
public void start(Stage primaryStage) throws Exception{
FlowPane root = new FlowPane(5, 5);
primaryStage.setTitle("Animal Sound");
String[] imgs = { "https://conserveblog.files.wordpress.com/2016/05/flagship-panda-thumbnail.jpeg?w=188", "http://news.bbc.co.uk/media/images/38625000/jpg/_38625095_021223panda150.jpg" };
for(String img: imgs){
FXMLLoader loader = new FXMLLoader( getClass().getResource("audio_button.fxml") );
Parent audioButton = loader.load();
AudioButtonController abc = loader.getController();
abc.setAudioLocation("not supported");
abc.setImageLocation(img);
root.getChildren().add(audioButton);
}
Scene scene = new Scene(root, 790, 675);
primaryStage.setScene(scene);
primaryStage.show();
}
}
This is a bit cumbersome for this example. If your button had more layout controls to it, and your outer layout was more complicated, then this could save some time.
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...
My problem is: I want to change something from the .fxml but whatever I do, nothing changes. This is just a simply example.
I went trough the whole internet but none of the solutions worked for me.
Here I want to change the text of the label, by calling the corresponding method from the main class.
Calling the same method (here setLabel()) when clicking a Button, with an event handler in the controller class, everything works fine, but a soon as I try to modify something from another class nothing works.
Main class:
package sample;
import javafx.application.Application;
import javafx.application.Platform;
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();
Controller controller = new Controller();
Platform.runLater(()->controller.setLabel());
}
FXML Code:
<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<center>
<Label fx:id="label" text="This" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
Controller class:
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller {
#FXML
private Label label=new Label();
public void setLabel(){
label.setText("Test");
}
}
There are actually two problems with your code.
1) In the start method of your Application you load sample.fxml using an FXMLLoader, which is correct. But creating a new controller like Controller controller = new Controller(); is incorrect, as you should get the controller from the FXMLLoader itself using getController method, and also you should not use the static load function of FXMLLoader, but you should create an instance of it.
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("sample.fxml"));
Controller controller = loader.getController();
When you call load on an FXMLLoader it will load the object hierarchy from the FXML file and it will also create a controller (referenced in the FXML file).
2) In Controller you inject the Label from FXML file, but you re-create it. When the FXMLLoader injects a control to your controller based on the fx:id it also ensures initialization. If you create a new Label it will not point to the Label instance created by the loader.
This
#FXML private Label label= new Label();
should be replaced with
#FXML private Label label;
That is pretty simple... You can change the the label text from your Main class either by adding a getter method in your Controller class for the required label and then get it in the Main class using the controller object (loader.getController()) and update its text. Or call the setter method inside the Controller class using controller object in Main.
As DVagra said, use loader.getController() to get the controller object.
(where loader is the object of FXMLoader).
Moreover you do not need Platform.runLater() to update the gui controls. As you're already running on the FX thread.
Anyway, here is what you need.
Main Class
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{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
Controller controller = loader.getController();
controller.setLabel("Test");
// Or
// controller.getLabel().setText("Test");
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class
package sample;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable{
#FXML
Label label;
//setter
public void setLabel(String labelText){
label.setText(labelText);
}
//getter for label
public Label getLabel() {
return label;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
Sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.60"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<center>
<Label fx:id="label" text="This" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
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
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);
}
}