Создание сложного интерфейса на java

Программирование JavaFX: разработка элементов интерфейса

Иногда даже программистам на Java необходимо создавать интерфейсы, и для этого им приходится изучать дополнительные инструменты. В этом случае им на помощь приходит инструментарий создания GUI, который избавляет от необходимости подключения дополнительных технологий — JavaFX.

Нужно сразу оговориться и сказать о том, что этот фреймворк — достаточно специфический и нишевый продукт, так как в настоящее время для создания интерфейсов обычно предпочитают использовать веб-подход и делать браузерные интерфейсы. Но минус этого подхода в том, что вам придётся разбираться в дополнительных технологиях, например, в том же самом JavaScript. В случае же с JavaFX вы продолжаете находиться в рамках знакомых вам технологий и сразу «визуально оживляете» их, что даже само по себе приятно наблюдать.

То есть в случае веб-подхода вы бы использовали, например, HTML, CSS, JavaScript; в случае же с JavaFX эта связка будет выглядеть как FXML, CSS, Java. Это означает, что на выходе мы получим десктопное приложение.

Что нужно для работы

Для работы с JavaFX вам понадобится установить две вещи: JDK Java и SDK JavaFX. Почему раздельно? Раньше всё это было несколько проще, и JavaFX шла в комплекте с основным JDK Java, сейчас же этот фреймворк распространяется в виде отдельного модуля, и вам надо будет скачать его отдельно.

Сказанное выше касается только тех ситуаций, когда вы не используете ранние версии Java вплоть до 10, так как в них JavaFX уже содержится в комплекте JDK.

Если же вы используете версию 11+, то у вас есть два варианта: либо качать SDK JavaFx и устанавливать зависимости вручную, либо воспользоваться любой системой сборки, например, Maven. Кстати говоря, если вы будете использовать именно Maven, то вам даже не придётся качать JavaFX, так как он загрузит необходимые модули самостоятельно.

Читайте также:  Double Range Slider

Вкратце пару слов про системы сборки и для чего это надо: в настоящее время, как правило, те компьютеры, на которых происходит разработка приложения, и те компьютеры, которые будут запускать приложения, — это разные машины.

Соответственно, после того как приложение было создано, для корректной работы требуется перенести всю структуру его папок и файлов на другой компьютер. Установить библиотеки, зависимости файлов друг от друга и очерёдность сборки. Можно это делать и вручную, но это будет «долго и печально», либо же использовать системы сборки, которые автоматически соберут проект для переноса. Соответственно, эти системы сборки должны знать, где и что у вас лежит, а также ряд другой информации. Именно под этим и подразумевается понятие «зависимостей».

С понятием зависимостей неразрывно связано и понятие архетипов, под которым подразумевается определённая конфигурация настройки. То есть это своего рода шаблон конфигурации, который можно применить к вашему проекту. Имея разные типы шаблонов, вы можете создавать различные типы проектов.

По ссылке выше (где описана установка с применением Maven) как раз говорится о настроенных архетипах для ускорения процесса создания проектов, которые предлагает разработчик JavaFX.

mvn archetype:generate \ -DarchetypeGroupId=org.openjfx \ -DarchetypeArtifactId=javafx-archetype-simple \ -DarchetypeVersion=0.0.3 \ -DgroupId=org.openjfx \ -DartifactId=sample \ -Dversion=1.0.0 \ -Djavafx-version=17.0.1

Структура приложений

Изначально в составе поставки JavaFX имеет большой инструментарий разнообразных элементов управления, таблиц и т.д, что облегчает работу с ним.

Если говорить о структуре типичного приложения, то оно представляет собой своеобразную матрёшку, представленную тремя уровнями: Stage, Scene, Node:

image

Источник картинки: Education wiki

То есть Stage — это то, что вмещает всё содержимое, внутри него находится как минимум один элемент Scene.

Все элементы Scene хранятся в виде так называемого Scene Graph, то есть в виде иерархии элементов.

На рисунке выше Node — это управляющие элементы, в качестве которых могут выступать как отдельные кнопки, так и макеты. Кроме того, может присутствовать вложенность одних элементов в другие.

Говоря об узле, можно увидеть, что на картинке он обозначен тремя идентификаторами: ROOT, BRANCH, LEAF.

  • ROOT — самый первый узел, который называется также первым графом сцены.
  • BRANCH — узел, у которого имеются производные дочерние узлы.
  • LEAF — конечный узел, у которого нет никаких дочерних. В качестве подобного примера можно назвать такие, как, например, box, rectangle.

image

Источник картинки: Vojtechruzicka

На картинке показано несколько Scene, так как мы уже говорили выше, что они могут переключаться между собой.

Чтобы проиллюстрировать всё вышесказанное, обратимся к документации, несмотря на то, что она относится к 8 версии, многое ещё применимо и в новых версиях. Например, нам необходимо создать простое приложение, которое выводит на экран какую-то фразу:

image

Источник картинки: Oracle

Если мы попробуем представить структуру этого приложения в виде схемы (Scene Graph, о котором мы уже говорили выше), то выглядеть это будет вот так:

image

Источник картинки: Oracle

Соответственно, код этого примера будет выглядеть следующим образом:

package helloworld; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class HelloWorld extends Application < @Override public void start(Stage primaryStage) < Button btn = new Button(); btn.setText("Say 'Hello World'"); btn.setOnAction(new EventHandler() < @Override public void handle(ActionEvent event) < System.out.println("Hello World!"); >>); StackPane root = new StackPane(); root.getChildren().add(btn); Scene scene = new Scene(root, 300, 250); primaryStage.setTitle("Hello World!"); primaryStage.setScene(scene); primaryStage.show(); > public static void main(String[] args) < launch(args); >>

Как вы видите, код создаёт кнопку, при нажатии на которую в консоль выводится надпись.

А допустим, нам необходимо некоторым образом организовать управляющие элементы, для этого служат, например, такие классы, как TilePane, GridPane, StackPane, BorderPane, AnchorPane, FlowPane, VBox, HBox:

image

Источник картинки: Metanit

Все эти панели компоновки входят в пакет javafx.scene.layout, описание классов которого находится по этому адресу:

  • TilePane — элементы в сетке из «плиток» одинакового размера.
  • GridPane — элементы в гибкой сетке строк и столбцов.
  • StackPane — элементы в стеке в обратном порядке.
  • BorderPane — элементы сверху, слева, справа, снизу и по центру.
  • AnchorPane — позволяет привязывать края дочерних узлов к смещению от краёв области привязки.
  • FlowPane — элементы в потоке, который обтекает границу области потока.
  • VBox — элементы в одном вертикальном столбце.
  • HBox — элементы в один горизонтальный ряд.

С применением такой компоновки можно создать достаточно красивые и сложные формы, например, регистрационную форму, подробный процесс создания которой рассмотрен здесь:

image

Источник картинки: Callicoder

Создание и настройка GridPane происходит вот таким образом:

 // Создание новой панели сетки GridPane gridPane = new GridPane(); // Положение панели в центре экрана как по вертикали, так и по горизонтали gridPane.setAlignment(Pos.CENTER); // Установка отступа по 20 пикселей с каждой стороны gridPane.setPadding(new Insets(40, 40, 40, 40)); // Установка горизонтального зазора между столбцами gridPane.setHgap(10); // Установка вертикального зазора между строками gridPane.setVgap(10); // columnOneConstraints будет применяться ко всем узлам, размещённым в первом столбце ColumnConstraints columnOneConstraints = new ColumnConstraints(100, 100, Double.MAX_VALUE); columnOneConstraints.setHalignment(HPos.RIGHT); // columnTwoConstraints будет применяться ко всем узлам, размещённым во втором столбце ColumnConstraints columnTwoConstrains = new ColumnConstraints(200,200, Double.MAX_VALUE); columnTwoConstrains.setHgrow(Priority.ALWAYS); gridPane.getColumnConstraints().addAll(columnOneConstraints, columnTwoConstrains);
import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.stage.Stage; import javafx.stage.Window; public class RegistrationFormApplication extends Application < @Override public void start(Stage primaryStage) throws Exception < primaryStage.setTitle("Registration Form JavaFX Application"); // Create the registration form grid pane GridPane gridPane = createRegistrationFormPane(); // Add UI controls to the registration form grid pane addUIControls(gridPane); // Create a scene with registration form grid pane as the root node Scene scene = new Scene(gridPane, 800, 500); // Set the scene in primary stage primaryStage.setScene(scene); primaryStage.show(); >private GridPane createRegistrationFormPane() < // Instantiate a new Grid Pane GridPane gridPane = new GridPane(); // Position the pane at the center of the screen, both vertically and horizontally gridPane.setAlignment(Pos.CENTER); // Set a padding of 20px on each side gridPane.setPadding(new Insets(40, 40, 40, 40)); // Set the horizontal gap between columns gridPane.setHgap(10); // Set the vertical gap between rows gridPane.setVgap(10); // Add Column Constraints // columnOneConstraints will be applied to all the nodes placed in column one. ColumnConstraints columnOneConstraints = new ColumnConstraints(100, 100, Double.MAX_VALUE); columnOneConstraints.setHalignment(HPos.RIGHT); // columnTwoConstraints will be applied to all the nodes placed in column two. ColumnConstraints columnTwoConstrains = new ColumnConstraints(200,200, Double.MAX_VALUE); columnTwoConstrains.setHgrow(Priority.ALWAYS); gridPane.getColumnConstraints().addAll(columnOneConstraints, columnTwoConstrains); return gridPane; >private void addUIControls(GridPane gridPane) < // Add Header Label headerLabel = new Label("Registration Form"); headerLabel.setFont(Font.font("Arial", FontWeight.BOLD, 24)); gridPane.add(headerLabel, 0,0,2,1); GridPane.setHalignment(headerLabel, HPos.CENTER); GridPane.setMargin(headerLabel, new Insets(20, 0,20,0)); // Add Name Label Label nameLabel = new Label("Full Name : "); gridPane.add(nameLabel, 0,1); // Add Name Text Field TextField nameField = new TextField(); nameField.setPrefHeight(40); gridPane.add(nameField, 1,1); // Add Email Label Label emailLabel = new Label("Email ID : "); gridPane.add(emailLabel, 0, 2); // Add Email Text Field TextField emailField = new TextField(); emailField.setPrefHeight(40); gridPane.add(emailField, 1, 2); // Add Password Label Label passwordLabel = new Label("Password : "); gridPane.add(passwordLabel, 0, 3); // Add Password Field PasswordField passwordField = new PasswordField(); passwordField.setPrefHeight(40); gridPane.add(passwordField, 1, 3); // Add Submit Button Button submitButton = new Button("Submit"); submitButton.setPrefHeight(40); submitButton.setDefaultButton(true); submitButton.setPrefWidth(100); gridPane.add(submitButton, 0, 4, 2, 1); GridPane.setHalignment(submitButton, HPos.CENTER); GridPane.setMargin(submitButton, new Insets(20, 0,20,0)); submitButton.setOnAction(new EventHandler() < @Override public void handle(ActionEvent event) < if(nameField.getText().isEmpty()) < showAlert(Alert.AlertType.ERROR, gridPane.getScene().getWindow(), "Form Error!", "Please enter your name"); return; >if(emailField.getText().isEmpty()) < showAlert(Alert.AlertType.ERROR, gridPane.getScene().getWindow(), "Form Error!", "Please enter your email id"); return; >if(passwordField.getText().isEmpty()) < showAlert(Alert.AlertType.ERROR, gridPane.getScene().getWindow(), "Form Error!", "Please enter a password"); return; >showAlert(Alert.AlertType.CONFIRMATION, gridPane.getScene().getWindow(), "Registration Successful!", "Welcome " + nameField.getText()); > >); > private void showAlert(Alert.AlertType alertType, Window owner, String title, String message) < Alert alert = new Alert(alertType); alert.setTitle(title); alert.setHeaderText(null); alert.setContentText(message); alert.initOwner(owner); alert.show(); >public static void main(String[] args) < launch(args); >>

Говоря об управляющих элементах (которые, как вы видели, использовались выше), например, о таких, как кнопки, мы фактически говорим об узле (Node). Примерный вид иерархии классов, которые наследуются от Node, можно увидеть на картинке ниже:

image

Источник картинки: Metanit

Полный же список элементов, где так или иначе используется Node, можно посмотреть здесь.

Говоря о тексте, который используется в различных элементах сцены, можно сказать, что к текстовым элементам возможно применять различные эффекты, так как они наследуются от Node.

Причём, учитывая, что сам этот класс наследуется от Shape, текстовые элементы могут содержать обводку или любую заливку, так же как и обычная фигура. Примеры подобной работы с текстом имеются здесь:

image

Источник картинки: Oracle

String family = "Helvetica"; double size = 50; TextFlow textFlow = new TextFlow(); textFlow.setLayoutX(40); textFlow.setLayoutY(40); Text text1 = new Text("Hello "); text1.setFont(Font.font(family, size)); text1.setFill(Color.RED); Text text2 = new Text("Bold"); text2.setFill(Color.ORANGE); text2.setFont(Font.font(family, FontWeight.BOLD, size)); Text text3 = new Text(" World"); text3.setFill(Color.GREEN); text3.setFont(Font.font(family, FontPosture.ITALIC, size)); textFlow.getChildren().addAll(text1, text2, text3); Group group = new Group(textFlow); Scene scene = new Scene(group, 500, 150, Color.WHITE); stage.setTitle("Hello Rich Text"); stage.setScene(scene); stage.show();

Богатые возможности JavaFx по созданию графических элементов позволяют делать, например, такие элементы, как круговые диаграммы, столбчатые и т.д.:

image

Источник картинки: Oracle

Говоря о графическом наполнении, нельзя не отметить, что в развитие фреймворка серьёзный вклад привносит сообщество энтузиастов, сложившееся вокруг этой технологии. Если взять те же самые диаграммы, одна из команд разработчиков предложила библиотеку, которая позволяет создавать диаграммы достаточно впечатляющего вида:

image

Источник картинки: Github HanSolo

image

Источник картинки: Github HanSolo

image

Источник картинки: Github HanSolo

Или, например, библиотека по генерации разнообразных часов и индикаторов:

image

Источник картинки: Github HanSolo

Другие интересные и впечатляющие примеры использования библиотек для создания полезных приложений вы можете найти в разделе Community.

Завершая этот рассказ, следует отметить, что мы рассмотрели ключевые моменты этого фреймворка лишь обзорно и «глубоко не углубляясь». Поэтому в рамках этого рассказа не был рассмотрен графический способ создания интерфейсов с помощью Scene Builder, а также мы не затронули использование специфической реализации XML для JavaFX — FXML.

Обо всём этом мы поговорим в следующий раз.

НЛО прилетело и оставило здесь промокод для читателей нашего блога:

— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS .

Источник

Оцените статью