External jar FXML Load Exception - javafx

I try to load a class located in an external jar using classloader. The class "FXMLbase" loads ok, but the error is triggered when the FXMLdocument.fxml try to instantiate the FXMLDocumentController. However when the "FXMLbase" is instantiate throught the JavaFXApplication5.java (located at the external jar) it works fine. Any Ideas?
Class Loader
File file = new File("C:/Users/Os/Dropbox/CODE_OS/JavaFXApplication5/dist/JavaFXApplication5.jar");
URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()}, this.getClass().getClassLoader());
Class c = clazzLoader.loadClass("javafxapplication5.FXMLbase");
PluginInterface fXMLbase = (PluginInterface) c.newInstance();
Parent loadScreen = fXMLbase.getRoot();
FXMLbase.java -- external jar --
public Parent getRoot() {
Parent root = null;
try {
System.out.println("Class Name:" + getClass().getName());
root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
} catch (IOException ex) {
Logger.getLogger(FXMLbase.class.getName()).log(Level.SEVERE, null, ex);
}
return root;
}
FXMLdocument.fxml -- external jar --
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="javafxapplication5.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="126" layoutY="90" onAction="#handleButtonAction" text="Click Me! app5" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
</children>
FXMLDocumentController.java -- external jar --
public class FXMLDocumentController implements Initializable{
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
JavaFxApplication5.java -- external jar --
public void start(Stage stage) throws Exception {
FXMLbase fXMLbase=new FXMLbase();
Parent root = fXMLbase.getRoot();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Error:
ago 28, 2014 2:26:16 PM javafxapplication5.FXMLbase getRoot
SEVERE: null
javafx.fxml.LoadException:
file:/C:/Users/Os/Dropbox/CODE_OS/JavaFXApplication5/dist/JavaFXApplication5.jar!/javafxapplication5/FXMLDocument.fxml:9
....
Caused by: java.lang.ClassNotFoundException: javafxapplication5.FXMLDocumentController

The FXMLLoader at some point has to load the controller class from the value of the fx:controller attribute in the root element of the FXML file. It looks like it is using the system class loader to do this: I think this is because the system class loader finds the FXMLLoader class and loads it, rather than the class loader used to load your FXMLBase class.
The only fix I can find for this is to explicitly set the controller class from the FXMLbase class, instead of specifying it in the FXML. This is a bit unsatisfactory; perhaps there is a better way I am missing.
Updated FXMLbase class:
public Parent getRoot() {
Parent root = null;
try {
System.out.println("Class Name:" + getClass().getName());
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
loader.setController(new FXMLDocumentController());
root = loader.load();
} catch (IOException ex) {
Logger.getLogger(FXMLbase.class.getName()).log(Level.SEVERE, null, ex);
}
return root;
}
and you need to remove the fx:controller attribute from the FXML file.

Related

NullPointerException passing a string from a window to another

Long story short, I searched for similar problems to mine, so I could troubleshoot this "by my own". I found these examples here and here, but none of them worked for me.
I need to pass to the next window a string that was typed in the TextField of the first window.
My main:
public class Main extends Application{
/**
* #param stage
* #throws java.lang.Exception
*/
#Override
public void start(Stage stage) throws Exception {
FXMLLoader floader = new FXMLLoader();
Parent root = FXMLLoader.load(getClass().getResource("java1.fxml"));
Java1Controller j1 = (Java1Controller)floader.getController();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
As I'm using FXML, here's the controller of Window1 (java1):
public class Java1Controller implements Initializable {
#FXML
private TextField tSend;
#FXML
private Button bSend;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void sendText(ActionEvent event) throws IOException {
FXMLLoader fLd = new FXMLLoader();
Parent novo = FXMLLoader.load(getClass().getResource("java2.fxml"));
Java2Controller j2 = (Java2Controller)fLd.getController();
j2.setLbText(tSend.getText());
Scene novScene = new Scene(novo);
Stage novStage = (Stage)((Node)(event.getSource())).getScene().getWindow();
novStage.setScene(novScene);
novStage.setResizable(false);
novStage.show();
}
}
And the controller of Window2 (java2):
public class Java2Controller implements Initializable {
#FXML
private Label lbText;
#FXML
private Button bOK;
/**
* Initializes the controller class.
* #param url
* #param rb
*/
/**
* #param string the string to set
*/
public void setLbText(String string) {
lbText.setText(string);
}
#FXML
private void closeJPane(ActionEvent event) {
Stage stage = (Stage)((Node)(event.getSource())).getScene().getWindow();
stage.close();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
The problem is pointed to the Java1Controller line where I setText with my custom function (j2.setLbText). Why? If it matters, I'm using Netbeans 8.2 and JavaFX Scene Builder 11.0.
FXML Java1:
<Pane maxHeight="252.0" maxWidth="388.0" minHeight="252.0" minWidth="388.0" prefHeight="252.0" prefWidth="388.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.Java1Controller">
<children>
<Label layoutX="126.0" layoutY="76.0" text="Insira texto aqui:">
<font>
<Font size="16.0" />
</font>
</Label>
<TextField fx:id="tSend" layoutX="109.0" layoutY="102.0" />
<Button fx:id="bSend" layoutX="169.0" layoutY="178.0" mnemonicParsing="false" onAction="#sendText" text="Send" />
</children>
</Pane>
FXML Java2:
<Pane maxHeight="388.0" maxWidth="252.0" minHeight="252.0" minWidth="388.0" prefHeight="252.0" prefWidth="388.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.Java2Controller">
<children>
<Label fx:id="lbText" layoutX="137.0" layoutY="106.0" text="Texto enviado">
<font>
<Font size="16.0" />
</font>
</Label>
<Button fx:id="bOK" layoutX="164.0" layoutY="178.0" mnemonicParsing="false" onAction="#closeJPane" prefHeight="26.0" prefWidth="61.0" text="OK" />
</children>
</Pane>
You're using static load methods to load the fxmls. This way a new FXMLLoader instance is created by the method that you cannot get a reference to. The FXMLLoader instance you create yourself is never used to load an fxml file, so no controller is available via its getController property:
// here you create a loader without setting a location
FXMLLoader fLd = new FXMLLoader();
// here a static method uses another loader instance to load the fxml
Parent novo = FXMLLoader.load(getClass().getResource("java2.fxml"));
// here you try to retrieve the controller from the loader instance that is still in the same state as after the constructor completion
Java2Controller j2 = (Java2Controller)fLd.getController();
Change your code an use the loader instance you created yourself to load the fxml:
// create loader with location set
FXMLLoader fLd = new FXMLLoader(getClass().getResource("java2.fxml"));
// use loader instance to load fxml from location specified
Parent novo = fLd.load();
// the controller should now be available
Java2Controller j2 = (Java2Controller)fLd.getController();
assert j2 != null

JavaFX fx:id doesn´t create a Object in the controller class

Hello I am new to JavaFX and have problems with the fx:id.
I get a null pointer exception in my controller class.
The objects apple and snake are not created, although the fx:id and declarations in the controller class match.
Can someone help me?
Main-Class:
public class Main extends Application {
private Controller controll = new Controller();
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("World2.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
scene.setOnKeyPressed(controll);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Controller-Class:
public class Controller implements EventHandler<Event> {
#FXML
private ImageView snake;
#FXML
private ImageView apple;
private int snakeRow = 8;
private int snakeColumn = 10;
private int appleRow = 5;
private int appleColumn = 5;
#Override
public void handle(Event ev) {
KeyEvent event = (KeyEvent) ev;
System.out.println("handle-Methode");
if (event.getCode() == KeyCode.LEFT && snakeColumn > 0) {
snakeColumn--;
System.out.println("Snake-ID: " + snake);
System.out.println("Apple-ID: " + apple);
}
}
FXML:
<ImageView fx:id="apple" fitHeight="39.0" fitWidth="44.0" pickOnBounds="true" GridPane.columnIndex="5" GridPane.rowIndex="5">
<image>
<Image url="#../img/icons8-apfel-48.png" />
</image>
</ImageView>
<ImageView fx:id="snake" fitHeight="36.0" fitWidth="44.0" pickOnBounds="true" GridPane.columnIndex="10" GridPane.rowIndex="8">
<image>
<Image url="#../img/icons8-hydra-48.png" />
</image>
</ImageView>
A controller instance is not associated with a scene unless
It's created by FXMLLoader based on the fx:controller attribute in the fxml. In this case you'd need to retrieve the controller instance from the FXMLLoader instance used to load the fxml after loading:
FXMLLoader loader = new FXMLLoader(getClass().getResource("World2.fxml"));
Parent root = loader.load();
Controller controll = loader.getController();
or
You don't specify the fx:controller attribute but pass a Object to FXMLLoader before loading that is to be used as controller for the scene:
FXMLLoader loader = new FXMLLoader(getClass().getResource("World2.fxml"));
loader.setController(controll);
Parent root = loader.load();

How can I create my own icon with propertise in JavaFX

Maybe somebody knows the answer and try help me.
I am creating own button.
<fx:root maxHeight="100.0" maxWidth="100.0" minHeight="50.0" minWidth="50.0" prefHeight="80.0" prefWidth="80.0" style="-fx-background-color: red;" type="StackPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
<children>
<ImageView fx:id="baseImage" fitHeight="66.0" fitWidth="72.0" pickOnBounds="true" preserveRatio="true" StackPane.alignment="TOP_CENTER" />
<Label fx:id="textBtn" alignment="BOTTOM_LEFT" prefHeight="17.0" prefWidth="75.0" textFill="WHITE" textOverrun="CLIP" StackPane.alignment="BOTTOM_LEFT" />
</children>
</fx:root>
So I need to change my button (Image and Label), when I am creating this in FXML file.
<MyButton layoutX="200.0" layoutY="162.0" />
e.g
<MyButton layoutX="200.0" layoutY="162.0" image="" text="" />
Can somebody help me ?
My Java Code
public class MyButton extends StackPane
{
#FXML
private ImageView baseImage;
#FXML
private Label textBtn;
public MyButton()
{
FXMLLoader fxmlLoader =new FXMLLoader(getClass().getResource("/pl/edu/wat/wcy/pz/icons/MyButtonView.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
init();
try {
fxmlLoader.load();
}
catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public Label getTextBtn() {
return textBtn;
}
public void setTextBtn(Label textBtn) {
this.textBtn = textBtn;
}
public ImageView getBaseImage() {
return baseImage;
}
public void setBaseImage(Image location) {
this.baseImage.setImage(location);
}
public void setButton(Label textBtn, Image location){
this.baseImage.setImage(location);
this.textBtn = textBtn;
}
But I care about icon are changed in FXML file, not JavaCode
}
If you want to set properties in FXML:
<MyButton layoutX="200.0" layoutY="162.0" image="" text="" />
you must define those properties in the corresponding class. In particular, MyButton must define setImage(...) and setText(...) methods (it already has setLayoutX(...) and setLayoutY(...) which are inherited from StackPane). It's hard to know exactly what functionality you want here, but you probably want to set these up as JavaFX Properties. If the intention is to map these into the Label and ImageView defined in the FXML file, you can just expose the relevant properties from the controls. You might need to work a bit to map the string from the image property into the correct thing.
public class MyButton extends StackPane
{
#FXML
private ImageView baseImage;
#FXML
private Label textBtn;
public MyButton()
{
FXMLLoader fxmlLoader =new FXMLLoader(getClass().getResource("/pl/edu/wat/wcy/pz/icons/MyButtonView.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
// not sure what this is:
// init();
// note that if you define
// public void initialize() {...}
// it will be called
// automatically during the FXMLLoader.load() method
try {
fxmlLoader.load();
}
catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public StringPropergty textProperty() {
return textBtn.textProperty();
}
public final String getText() {
return textProperty().get();
}
public final void setText(String text) {
textProperty().set(text);
}
// similarly expose a property for image, but you need to be able to coerce it from a String
}
(Incidentally, I assume this is just an example for the purposes of understanding how to do this. Everything you have in the example can be done using a regular button. Just wanted to make that clear for any others reading this post.)

Javafx - how to modify components in fxml file after loading it

I am a novice to javafx. I created a simple javafx fxml application. I defined a treeView in my fxml file. It is as follows,
FXMLDocument.fxml
<AnchorPane id="AnchorPane" prefHeight="200.0" prefWidth="320.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="scenebuilderfirst.FXMLDocumentController">
<children>
<TreeView id="" fx:id="tree" layoutX="60.0" layoutY="14.0" prefHeight="172.0" prefWidth="200.0" />
</children>
</AnchorPane>
and created the controller class as follows,
FXMLDocumentController.java
public class FXMLDocumentController implements Initializable {
#FXML
private TreeView<?> tree;
#Override
public void initialize(URL url, ResourceBundle rb) {
}
}
my main java file is as follows,
MyApp.java
public class SceneBuilderFirst extends Application {
#Override
public void start(Stage stage) throws Exception {
AnchorPane root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Now I need to add tree items to the TreeView while running the program. So I thought of writing the following peace of code inside the initialize() method of my contorller class,
// creating root item for the tree
TreeItem<String> rootItem = new TreeItem<>("root");
rootItem.setExpanded(true);
//creating childern for the tree
TreeItem<String>item1= new TreeItem<>("content1");
rootItem.getChildren().add(item1);
TreeItem<String>item2= new TreeItem<>("content2");
rootItem.getChildren().add(item2);
//attaching root item to tree
tree = new TreeView<>(rootItem);
But it didn't work.
I need to set the TreeItems while running the program. Not through the fxml file.
So I think I should do the coding in my main java file
I need a way to access my TreeView after loading it via FXMLLoader in my main java file so i can make a TreeView on my own choice and attach it to this TreeView while running the program. Please guide me.
To fix this, replace
tree = new TreeView<>(rootItem);
with
tree.setRoot(rootItem);
For an explanation of why, see this discussion.
You also have to fix your declaration of the TreeView:
#FXML
private TreeView<String> tree;

Java FX Getting a custom control working using multiple FXML files

I am trying to work with custom controls in JavaFX and multiple FXML files. I create a custom control:
public class PetListGUIManager extends BorderPane
{
#FXML ListView<String> petList;
private PetManager pm;
public PetListGUIManager()
{
try
{
FXMLLoader loader = new FXMLLoader( getClass().getResource("PetList.fxml"));
loader.setRoot( this );
loader.setController( this );
loader.load();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
pm = new PetManager();
ObservableList<String> items = FXCollections.observableArrayList( pm.getDisplayablePets() );
petList.setItems( items );
}
#FXML
public void handleButtonAction( ActionEvent event )
{
Button b = (Button) event.getSource();
b.setText("Clicked!");
}
}
Using this FXML file:
<fx:root type="javafx.scene.layout.BorderPane" xmlns:fx="http://javafx.com/fxml">
<bottom>
<Button onAction="#handleButtonAction" text="Click Me!" />
</bottom>
<center>
<ListView fx:id="petList" prefHeight="200.0" prefWidth="200.0" />
</center>
</fx:root>
Then I use this other main program:
public class TestMain extends Application
{
PetListGUIManager pm;
#FXML FlowPane mainPane;
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader
.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Test Main");
stage.setScene(scene);
stage.show();
pm = new PetListGUIManager();
}
/**
* Purpose:
* #param args
*/
public static void main(String[] args)
{
Application.launch( args );
}
#FXML
public void doSomething( ActionEvent ae )
{
System.out.println( "In do something: " + mainPane );
ObservableList<Node> children = mainPane.getChildren( );
System.out.println( "Children are " + children );
children.add( pm );
}
}
Which uses this FXML file:
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="TestMain">
<children>
<FlowPane fx:id="mainPane" prefHeight="400.0" prefWidth="600.0" >
<Button text="Click here to start" onAction="#doSomething"/>
</FlowPane>
</children>
</AnchorPane>
When I click the button in the main window - which should load the custom control I get a java.lang.reflect.InvocationTargetException exception caused by: java.lang.NullPointerException: Children: child node is null: parent = FlowPane[id=mainPane]
What am I missing here?
Second question: You may notice I arbitrarily added a button to my main window so when it was clicked I could then load my BorderPane custom control class. Ideally I would like to do this directly in the start method of TestMain but mainPane is null in the start method - when does it become not null?
You should never make your main Application class a controller - it's just too confusing and fraught with errors such as you have encountered.
What is happening is that when you load Main.fxml, the FXMLLoader will create a new instance of the Application class. When you click on the button defined in the main fxml UI, the doSomething method will be invoked on that new TestMain object, not the original TestMain object that was created when your application was launched.
The solution is to create a MainController.java which functions as the controller for the Main.fxml GUI (similar to what you have already done with PetListGUIManager) and remove any of the #FXML related notation completely from your main application class.

Resources