7

I want to create a custom control in an executable jar file and then use in Gluon Scene Builder 11.00. I need to know how to do this. I tried several forms but my controls not appear when import jar in Scene Builder. I am using IntelliJ Community Edition 2019.2 and Gluon Scene Builder 11.00 and Java 12.0.2. Anyone please that could help me with a little example?

Maven Project.

custom_control.fxml

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>

<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="138.0" type="VBox" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <TextField fx:id="textField" />
      <Button mnemonicParsing="false" onAction="#doSomething" prefHeight="25.0" prefWidth="142.0" text="Clck Me" />
   </children>
</fx:root>

CustomControl.java

package customcontrolexample;

import java.io.IOException;

import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class CustomControl extends VBox {
    @FXML private TextField textField;

    public CustomControl() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
                "custom_control.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

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

    public String getText() {
        return textProperty().get();
    }

    public void setText(String value) {
        textProperty().set(value);
    }

    public StringProperty textProperty() {
        return textField.textProperty();
    }

    @FXML
    protected void doSomething() {
        System.out.println("The button was clicked!");
    }
}

CustomControlExample.java

import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class CustomControlExample extends Application {

    private static Scene scene;

    @Override
    public void start(Stage stage) throws IOException {
        CustomControl customControl = new CustomControl();
        customControl.setText("Hello!");
        stage.setScene(new Scene(customControl));
        stage.setTitle("Custom Control");
        stage.setWidth(300);
        stage.setHeight(200);
        stage.show();
    }

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

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>customcontrol</groupId>
    <artifactId>CustomControlExample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>12.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>12.0.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.3</version>
                <configuration>
                    <mainClass>CustomControlExample</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.1.1</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>CustomControlExample</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
                        <phase>package</phase> <!-- bind to the packaging phase -->
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/198563/discussion-on-question-by-gustavo-gregorio-creating-custom-control-in-jar-for-gl). – Samuel Liew Aug 28 '19 at 02:45

1 Answers1

6

There are a few things to consider when you want to create a custom control that can be imported with Scene Builder.

What qualifies as custom control

There is no documentation about this, but you can have a look at the current source code in Scene Builder that explores all the classes in a jar, and finds valid classes as custom controls:

  • Classname not starting with java., javax., javafx., com.oracle.javafx.scenebuilder., com.javafx., com.gluonhq.
  • Concrete class (not abstract)
  • Assignable from Node. This is quite important: the class should be a subclass of a JavaFX node, like a container (i.e. extends from VBox) or a built-in node (i.e. extends from Button).

For all the classes found, then an FXML is built with something like:

 <?import your.class.fullname?>
 <your.class.simplename />

If that FXML can be loaded by the FXMLLoader, then it is a custom control. Otherwise it is discarded.

Type of custom control

There are several approaches on how to build a custom control, but if you are going to use FXML this old tutorial gives some important clarification on how to use fx:root and how to define the controller, like:

public class CustomControl extends VBox {

    public CustomControl() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
"custom_control.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

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

Java 11 level

Finally, since Scene Builder 11 runs on Java 11, the jar has to be compiled with support for Java 11.

Using Maven:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
          <source>11</source>
          <target>11</target>
    </configuration>
</plugin>

Before going to Scene Builder, you can try your custom control in a demo project. This will help you find and solve possible issues in advance.

Scene Builder 11

Once you have built the control, you can open Scene Builder 11 and import it following the documentation.

Basically, using the option Add Library/FXML from file system and browsing your file system to locate the jar.

Note: there is a merged PR that will allow using the build/target directory of a project, instead of requiring a jar.

In case of the posted custom control, you should get this:

custom control

If the view doesn't show your control, there is an issue that you have to solve first.

Note: there is a pending PR that will provide more information when the process fail.

José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • 2
    For additional, information you can also refer to the answer to a related stack overflow question on [How to Import a Component from a JAR into SceneBuilder](https://stackoverflow.com/questions/29444698/how-to-create-an-fxml-file-for-an-already-created-new-component-in-java-than-add) – jewelsea Aug 28 '19 at 17:32