Составные пользовательские компоненты JavaFX GUI-интерфейса можно создавать путем разработки Java-класса, расширяющего класс javafx.scene.Parent, который является базовым классом узлов графа сцены, имеющих дочерние узлы. При этом класс пользовательского компонента можно адаптировать для использования в FXML-описании графа сцены приложения.

Однако пользовательский JavaFX-компонент можно также создать, разделив его на FXML-описание составных частей компонента и Java-класс контроллер, соединяющий FXML-описание компонента и его логику.
 
Для создания FXML-описания составных частей компонента удобно использовать инструмент Scene Builder (http://docs.oracle.com/javafx/scenebuilder/1/installation/jsbpub-installation.htm), обеспечивающий визуальное редактирование графа сцены.
 
В качестве примера рассмотрим использование пользовательского компонента кубической формы с гиперссылкой.
 
После визуального создания инструментом Scene Builder FXML-описания 3D-компонента, изменим корневой элемент FXML-описания на <fx:root>:
 
<?xml version="1.0" encoding="UTF-8"?>
 
 
 
 
<?import java.lang.*?>
 
<?import java.util.*?>
 
<?import javafx.geometry.*?>
 
<?import javafx.scene.*?>
 
<?import javafx.scene.control.*?>
 
<?import javafx.scene.layout.*?>
 
<?import javafx.scene.shape.*?>
 
<?import javafx.scene.text.*?>
 
 
 
 
<fx:root type="javafx.scene.Group" xmlns:fx="http://javafx.com/fxml" >
 
<children>
 
<Rectangle fill="DARKBLUE" height="50.0" rotate="90.0" translateX="-25.0" translateY="-50.0" width="100.0">
 
<rotationAxis>
 
<Point3D x="1.0" />
 
</rotationAxis>
 
</Rectangle>
 
<Rectangle fill="DARKBLUE" height="50.0" rotate="90.0" translateX="-50.0" translateY="-25.0" width="50.0">
 
<rotationAxis>
 
<Point3D y="1.0" />
 
</rotationAxis>
 
</Rectangle>
 
<Rectangle fill="DARKBLUE" height="50.0" rotate="90.0" translateX="50.0" translateY="-25.0" width="50.0">
 
<rotationAxis>
 
<Point3D y="1.0" />
 
</rotationAxis>
 
</Rectangle>
 
<Rectangle fill="DARKBLUE" height="50.0" rotate="90.0" translateX="-25.0" width="100.0">
 
<rotationAxis>
 
<Point3D x="1.0" />
 
</rotationAxis>
 
</Rectangle>
 
<Rectangle fx:id="backface" fill="DARKBLUE" height="50.0" translateX="-25.0" translateY="-25.0" translateZ="25.0" width="100.0"/>
 
<StackPane id="StackPane" translateX="-25.0" translateY="-25.0" translateZ="-25.0">
 
<children>
 
<Rectangle fill="BLUE" height="50.0" width="100.0" />
 
<Hyperlink fx:id="link" textFill="WHITE">
 
<font>
 
<Font name="Georgia" size="18.0" />
 
</font>
 
</Hyperlink>
 
</children>
 
</StackPane>
 
</children>
 
</fx:root>
 
Создадим Java-класс, загружающий FXML-описание компонента и определяющий его поведение:
 
package javafxogwebfxmlscenebuilder;
 
 
 
 
import java.io.IOException;
 
import javafx.animation.Animation;
 
import javafx.animation.KeyFrame;
 
import javafx.animation.KeyValue;
 
import javafx.animation.Timeline;
 
import javafx.event.EventHandler;
 
import javafx.fxml.FXML;
 
import javafx.fxml.FXMLLoader;
 
import javafx.scene.Group;
 
import javafx.scene.control.Hyperlink;
 
import javafx.scene.input.MouseEvent;
 
import javafx.scene.transform.Rotate;
 
import javafx.util.Duration;
 
 
 
 
public class MenuContainer extends Group{
 
 
 
@FXML private Hyperlink link;
 
final Rotate rx = new Rotate(0,Rotate.X_AXIS);
 
final Rotate ry = new Rotate(0,Rotate.Y_AXIS);
 
final Rotate rz = new Rotate(0,Rotate.Z_AXIS);
 
 
 
public MenuContainer(){
 
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MenuContainer.fxml"));
 
fxmlLoader.setRoot(this);
 
fxmlLoader.setController(this);
 
 
 
 
try {
 
fxmlLoader.load();
 
} catch (IOException exception) {
 
throw new RuntimeException(exception);
 
}
 
final Group group=this;
 
rx.setAngle(15);
 
ry.setAngle(15);
 
rz.setAngle(0);
 
this.getTransforms().addAll(rz, ry, rx);
 
 
 
 
final Timeline animation = new Timeline();
 
animation.getKeyFrames().addAll(
 
new KeyFrame(Duration.ZERO, new KeyValue(rx.angleProperty(), 15d)),
 
new KeyFrame(new Duration(1000), new KeyValue(rx.angleProperty(), 375d))
 
);
 
animation.setCycleCount(Animation.INDEFINITE);
 
 
 
group.setOnMouseDragged(new EventHandler<MouseEvent>() {
 
@Override
 
public void handle(MouseEvent event) {
 
animation.play();
 
double x= event.getSceneX();
 
double y = event.getSceneY();
 
group.setLayoutX(x);
 
group.setLayoutY(y);
 
group.setOnMouseReleased(new EventHandler<MouseEvent>() {
 
@Override
 
public void handle(MouseEvent event) {
 
animation.stop();
 
rx.angleProperty().set(15);
 
}
 
});
 
} });
 
}
 
 
 
public String getLabel(){
 
return link.textProperty().get();
 
}
 
public void setLabel(String value){
 
link.textProperty().setValue(value);
 
}
 
}
 
Теперь созданный компонент можно использовать в 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.*?>
 
<?import javafxogwebfxmlscenebuilder.*?>
 
 
 
 
<AnchorPane id="AnchorPane" prefHeight="600" prefWidth="800" xmlns:fx="http://javafx.com/fxml" >
 
<children>
 
<MenuContainer layoutX="50" layoutY="50" label="Home" />
 
<MenuContainer layoutX="160" layoutY="50" label="Services" />
 
<MenuContainer layoutX="270" layoutY="50" label="Blog" />
 
<MenuContainer layoutX="380" layoutY="50" label="Contacts" />
 
</children>
 
</AnchorPane>
 
 
 
 
import javafx.application.Application;
 
import javafx.fxml.FXMLLoader;
 
import javafx.scene.Parent;
 
import javafx.scene.Scene;
 
import javafx.scene.paint.Color;
 
import javafx.stage.Stage;
 
 
 
 
public class JavaFXOGWebFXMLSceneBuilder extends Application {
 
 
 
@Override
 
public void start(Stage stage) throws Exception {
 
Parent root = FXMLLoader.load(getClass().getResource("OGWeb.fxml"));
 
 
 
Scene scene = new Scene(root);
 
scene.setFill(Color.BLACK);
 
stage.setScene(scene);
 
stage.show();
 
}
 
 
 
public static void main(String[] args) {
 
launch(args);
 
}
 
}