Для использования пользовательского JavaFX-компонента в FXML-описании графа сцены требуется его адаптация.
 
При объявлении класса JavaFX-компонента в FXML-описании должен существовать способ автоматического создания экземпляра класса. Поэтому класс JavaFX-компонента должен либо иметь конструктор по умолчанию без аргументов, либо иметь статический метод-фабрику без аргументов для создания экземпляра класса, либо иметь класс, реализующий интерфейс javafx.util.Builder, и класс, реализующий интерфейс javafx.util.BuilderFactory.
Для передачи какого-либо параметра из FXML-описания в созданный экземпляр класса JavaFX-компонента в классе должна присутствовать пара методов get/set параметра.
 
В качестве примера рассмотрим использование пользовательского компонента кубической формы с гиперссылкой.
 
Главный класс приложения загружает FXML-описание графа сцены:
 
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 JavaFXOGWebFXML 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);
 
}
 
}
 
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 javafxogwebfxml.*?>
 
 
 
 
<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>
 
Здесь объявляется пользовательский компонент MenuContainer, в экземпляр которого передается параметр label. В данном случае класс компонента MenuContainer имеет конструктор по умолчанию без аргументов:
 
import javafx.animation.Animation;
 
import javafx.animation.KeyFrame;
 
import javafx.animation.KeyValue;
 
import javafx.animation.Timeline;
 
import javafx.event.EventHandler;
 
import javafx.scene.Group;
 
import javafx.scene.control.Hyperlink;
 
import javafx.scene.control.HyperlinkBuilder;
 
import javafx.scene.input.MouseEvent;
 
import javafx.scene.layout.StackPane;
 
import javafx.scene.paint.Color;
 
import javafx.scene.shape.Rectangle;
 
import javafx.scene.shape.RectangleBuilder;
 
import javafx.scene.text.Font;
 
import javafx.scene.transform.Rotate;
 
import javafx.util.Duration;
 
 
 
 
public class MenuContainer extends Group {
 
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);
 
private double size=50.0;
 
private Color colorContainer=Color.BLUE;
 
private double shade=2.0;
 
private Hyperlink link;
 
private Color colorLabel=Color.WHITE;
 
private Font font=Font.font("Georgia", 18);
 
 
 
public MenuContainer() {
 
super();
 
rx.setAngle(15);
 
ry.setAngle(15);
 
rz.setAngle(0);
 
this.getTransforms().addAll(rz, ry, rx);
 
final Group group=this;
 
//
 
final Rectangle backFace = RectangleBuilder.create() // back face
 
.width(size*2).height(size)
 
.fill(colorContainer.deriveColor(0.0, 1.0, (1 - 0.3*shade), 1.0))
 
.translateX(-0.5*size)
 
.translateY(-0.5*size)
 
.translateZ(0.5*size)
 
.build();
 
//
 
final Rectangle bottomFace = RectangleBuilder.create() // bottom face
 
.width(size*2).height(size)
 
.fill(colorContainer.deriveColor(0.0, 1.0, (1 - 0.3*shade), 1.0))
 
.translateX(-0.5*size)
 
.translateY(0)
 
.rotationAxis(Rotate.X_AXIS)
 
.rotate(90)
 
.build();
 
//
 
final Rectangle rightFace = RectangleBuilder.create() // right face
 
.width(size).height(size)
 
.fill(colorContainer.deriveColor(0.0, 1.0, (1 - 0.3*shade), 1.0))
 
.translateX(1*size)
 
.translateY(-0.5*size)
 
.rotationAxis(Rotate.Y_AXIS)
 
.rotate(90)
 
.build();
 
//
 
final Rectangle leftFace = RectangleBuilder.create() // left face
 
.width(size).height(size)
 
.fill(colorContainer.deriveColor(0.0, 1.0, (1 - 0.3*shade), 1.0))
 
.translateX(-1*size)
 
.translateY(-0.5*size)
 
.rotationAxis(Rotate.Y_AXIS)
 
.rotate(90)
 
.build();
 
//
 
final Rectangle topFace = RectangleBuilder.create() // top face
 
.width(size*2).height(size)
 
.fill(colorContainer.deriveColor(0.0, 1.0, (1 - 0.2*shade), 1.0))
 
.translateX(-0.5*size)
 
.translateY(-1*size)
 
.rotationAxis(Rotate.X_AXIS)
 
.rotate(90)
 
.build();
 
//
 
link = HyperlinkBuilder.create()
 
.textFill(colorLabel)
 
.font(font)
 
.build();
 
final StackPane face = new StackPane();
 
face.getChildren().addAll(RectangleBuilder.create() // face
 
.width(size*2).height(size)
 
.fill(colorContainer)
 
.build(),
 
link
 
);
 
face.setTranslateX(-0.5*size);
 
face.setTranslateY(-0.5*size);
 
face.setTranslateZ(-0.5*size);
 
 
 
link.setOnMouseClicked(new EventHandler<MouseEvent>() {
 
@Override
 
public void handle(MouseEvent event) {
 
 
 
} });
 
 
 
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);
 
}
 
});
 
} });
 
//
 
getChildren().addAll(backFace,bottomFace,rightFace,leftFace,topFace,face);
 
}
 
 
 
public String getLabel(){
 
return link.textProperty().get();
 
}
 
public void setLabel(String value){
 
link.textProperty().setValue(value);
 
}
 
}