Содержание

JavaFX

Сведения

:!: Сведения

Stage - окружающее окно, которое содержит в себе остальные элементы. Может быть несколько, но должно быть как минимум одно. Является основным контейнером и точкой входа

Scene - отображает содержание stage. Stage может содержать несколько scene, которые можно между собой переключать. Внутри реализуется графом объектов (Scene Graph), где каждый элемент является узлом (node)

Node - элемент управления, например кнопки, метки или даже макеты (layout), внутри которых может быть несколько вложенных компонентов. У каждого узла свой идентификатор, стиль и т.д.

 

Использование

Есть как минимум пару вариантов использования, которые несколько разнятся

:!: Интерфейс в fxml файлах

Простая форма fxml
(myApp/src/main/resources/maket/rootWindow.fxml)

<?xml version="1.0" encoding="UTF-8"?>
 
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
 
<VBox alignment="CENTER" prefHeight="300.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sakeeper.sakeeper.controller.BaseController">
    <padding>
        <Insets bottom="10" top="5" />
    </padding>
    <Label alignment="TOP_CENTER" text="Название торговой точки" />
    <HBox alignment="BOTTOM_CENTER" spacing="5">
        <padding>
            <Insets bottom="5" top="5" />
        </padding>
        <Button onAction="#product1" text="Product 1" />
        <Button onAction="#product2" text="Product 2" />
    </HBox>
   <HBox id="newHBox" prefHeight="100.0" prefWidth="200.0" />
</VBox>

Контроллер
(myApp/src/main/java/com/myApp/myApp/controller/BaseController.java)

package com.sakeeper.sakeeper.controller;
 
import com.sakeeper.sakeeper.Sakeeper;
import com.sakeeper.sakeeper.model.Product;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
 
public class BaseController {
    private Sakeeper sakeeper;
    @FXML
    private Button product1;
    @FXML
    private Button product2;
    public void setSakeeper(Sakeeper sakeeper){
        this.sakeeper = sakeeper;
    }
 
    @FXML
    private void product1(){
        sakeeper.showVolumeWindow(new Product("product_1"));
    }
    @FXML
    private void product2(){
        sakeeper.showVolumeWindow(new Product("product_2"));
    }
}

Основной код, главный файл

package com.sakeeper.sakeeper;
 
import com.sakeeper.sakeeper.controller.BaseController;
import com.sakeeper.sakeeper.controller.VolumeController;
import com.sakeeper.sakeeper.model.Product;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
 
import javafx.event.EventHandler;
 
import java.io.IOException;
 
public class Sakeeper extends Application {
    private Stage primaryStage;
    private VBox rootLayout;
 
    public Sakeeper(){
        // list add's
    }
 
    public static void main(String[] args) {
        launch();
    }
 
    @Override
    public void start(Stage primaryStage) throws IOException {
        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("saKeeper");
        showBaseWindow();
    }
 
    // Это основное окно
    public void showBaseWindow(){
        try{
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(Sakeeper.class.getResource("/maket/rootWindow.fxml"));
            rootLayout = loader.load();
 
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);
 
            BaseController controller = loader.getController();
            controller.setSakeeper(this);
            primaryStage.show();
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    // Это уже второе окно, в примере его нет
    public void showVolumeWindow(Product selectedProduct){
        try{
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(Sakeeper.class.getResource("/maket/volumeWindow.fxml"));
            VBox page = loader.load();
 
            Stage dialogStage = new Stage();
            dialogStage.setTitle("Выберите объем");
            dialogStage.initModality(Modality.WINDOW_MODAL);
            dialogStage.initOwner(primaryStage);
            dialogStage.setScene(new Scene(page));
 
            VolumeController volumeController = loader.getController();
            volumeController.setDialogStage(dialogStage);
            volumeController.setProduct(selectedProduct);
            dialogStage.showAndWait();
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
:!: Интерфейс в коде

Элементы так же могут создаваться в коде, размещаться на форме и задаваться свойства

    public void showBaseWindow(){
        try{
            (..) 
            Button newButt = new Button("This dynamic button");
            /*newButt.setOnAction(event -> {
                Alert message = new Alert(Alert.AlertType.INFORMATION);
                message.setContentText("this message from dynamic button ");
                message.show();
            });*/
 
 
            /*EventHandler<MouseEvent> rightClickHandler = event -> {
                if (MouseButton.SECONDARY.equals(event.getButton())) {
                    button.setFont(new Font(button.getFont().getSize() + 1));
                }
            };*/
 
            //Create the EventHandler
            EventHandler<ActionEvent> hnd = new EventHandler<ActionEvent>() {
                @Override
                //handle method
                public void handle(ActionEvent ev) {
                    Alert message = new Alert(Alert.AlertType.INFORMATION);
                    message.setContentText("this message from dynamic button ");
                    message.show();
                }
            };
            newButt.setOnAction(hnd);
            rootLayout.getChildren().add(newButt);
        (...)
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Оформление

Хорошее разделение должно придерживаться пути:

В JavaFX не совсем тот самый общеизвестный CSS, имеет собственные свойства
Выбор компонентов либо селектором (название типа компонента, с точкой вначале, либо ИД с решеткой вначале) аналогично CSS

:!: Примеры
.button {
    -fx-font-size: 15px;
}
 
.label {
    // Some properties
}
 
#my-component {
  ...
}
<Label styleClass="my-label,other-class">I am a simple label</Label>
   # или в java
Label label = new Label("I am a simple label");
label.getStyleClass().addAll("my-label", "other-class");
 
 
<Label fx:id="foo">I am a simple label</Label>
  #
Label label = new Label("I am a simple label");
label.setId("foo");

Подключение

<BorderPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            stylesheets="styles.css"
            ...
            >
  ...
</BorderPane>
 
  #
String stylesheet = getClass().getResource("/styles.css").toExternalForm();
scene.getStylesheets().add(stylesheet);
 
  #
<HBox stylesheets="styles.css">
    ...
</HBox>
 
  #
HBox box = new HBox();
String stylesheet = getClass().getResource("/styles.css").toExternalForm();
box.getStylesheets().add(stylesheet);