3

I've been toying with JavaFX for a while now and now I wanted to share some of the components I have built. For ease of use, I would like to be able to distribute the components as libraries, so another user can just import my libraries into JavFX project. However, building a a library file to import into SceneBuilder (SB) proved to be much harder than I had expected. I tried following this blog post but I must be doing something wrong. I want to create my own custom component, using a .fxml file and a .jar file.

I tried building this component:

TestPane.java

package pack;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.AnchorPane;

import java.io.IOException;

public class TestPane extends AnchorPane {
    public TestPane(){
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TestPane.fxml"));
        fxmlLoader.setRoot(this); 
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @FXML protected void onClick(ActionEvent ae){
        System.out.println("Click");
    }
}

TestPane.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<fx:root type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">

    <Button fx:id="button" layoutX="6.0" layoutY="10.0" mnemonicParsing="false" text="Button" onAction="#onClick" />

</fx:root>

I use Eclipse Mars, created a new JavaFX Library project, created a new package called pack. In that package, I created the two components from above. I then right-clicked the project and selected "Export... -> Jar-file", and made no changes in the export dialogue.

enter image description here

I then created a new FXML project. I opened the .fxml file in SB and imported my exported .jar file, using the Import JAR/FXML File..." dialogue, and then dragged it into my view.

enter image description here

Here is the code for the test project I've created.

LibTest.java package test;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class LibTest extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load( getClass().getResource("LibTest.fxml") );    
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public static void main(String[] args) {
        launch(args);
    }
}

LibTest.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import pack.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane prefHeight="199.0" prefWidth="126.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
   <children>
      <TestPane layoutX="34.0" layoutY="82.0" />
   </children>
</AnchorPane>

I thought I had followed all the online guides and tutorials by now but there is something I must be missing. When I then try to launch my test project, I get the error bellow. I understand that Caused by: javafx.fxml.LoadException: TestPane is not a valid type. is the "key message", but what does it mean, and how do I fix this problem?

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
    at com.sun.javafx.application.LauncherImpl$$Lambda$50/1645995473.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javafx.fxml.LoadException: TestPane is not a valid type.
/D:/WorkspaceDir/ProjectDir/bin/test/LibTest.fxml:11

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.createElement(FXMLLoader.java:2778)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2708)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3218)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3128)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3108)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3101)
    at test.LibTest.start(LibTest.java:15)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$53/198061478.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/186276003.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$48/1079803749.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/237061348.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$145(WinApplication.java:101)
    at com.sun.glass.ui.win.WinApplication$$Lambda$36/2117255219.run(Unknown Source)
    ... 1 more
Exception running application test.LibTest

What is it that I am missing? What causes this problem and how do I fix it?

Gikkman
  • 752
  • 6
  • 21
  • Did you get past this? This is pretty cool that it's even possible to do, many (most?) SDKs out there don't even really support custom components like this. – User Jul 14 '16 at 00:41
  • Yeah, I got it working. See the answer bellow. Or were you wondering about anything specific? – Gikkman Jul 23 '16 at 03:06

1 Answers1

1

The problem is caused by the compiler (or the VM, I don't know the proper timing) does not know how to create an object of type TestPane.

Note from the error log that it says that the error occurred at D:/WorkspaceDir/ProjectDir/bin/test/LibTest.fxml:11. If we look at the LibTest.fxml, row 11, we see that this is the row that tries to create the TestPane.

The solution to the problem is to add the library file into the build path of the Java project. This will give your project the proper knowledge of how to create an object of type TestPane. Right-click the project -> Build Path -> Configure Build Path... Then, under the Libraries-tab, click Add JARs... (or Add External JARs..., if the .jar file is not located within your workspace) and find your exported .jar file.

enter image description here

Gikkman
  • 752
  • 6
  • 21
  • Also, refrain from using capital letters in package names, if you want to create SceneBuilder libraries. I've had problems before with importing libraries that use capital names in their packages. – Gikkman Aug 31 '15 at 10:20