Stage - окружающее окно, которое содержит в себе остальные элементы. Может быть несколько, но должно быть как минимум одно. Является основным контейнером и точкой входа
Scene - отображает содержание stage. Stage может содержать несколько scene, которые можно между собой переключать. Внутри реализуется графом объектов (Scene Graph), где каждый элемент является узлом (node)
Node - элемент управления, например кнопки, метки или даже макеты (layout), внутри которых может быть несколько вложенных компонентов. У каждого узла свой идентификатор, стиль и т.д.
Есть как минимум пару вариантов использования, которые несколько разнятся
Простая форма 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);