T.M. SoftStudio

feci quod potui, faciant meliora potentes

JavaFX: Пользовательские GUI-компоненты и FXML

 

Для использования пользовательского 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);

}

}