Перейти к основному содержимому

JavaFX и GUI

Разработчику Архитектору

JavaFX и GUI

Desktop GUI в Java — JavaFX, Swing и альтернативы

После консольных программ (первая программа, основные конструкции) часто нужен графический интерфейс — окно, кнопки, формы, диалоги. В Java для этого есть несколько поколений библиотек — Swing в составе JDK, JavaFX (OpenJFX) как рекомендуемый современный стек, плюс специализированные фреймворки.

Эта статья — обзор экосистемы и примеры виджетов; пошаговый старт — Первая программа на JavaFX, рецепты по каждому элементу UIСправочник по JavaFX и Swing — элементы UI. Swing без Maven — практические примеры; полный Maven-проектпрактикум "XML-валидатор"; галерея с построчным разбором для лабораторных — Lab — Java Swing. Общая картина платформы — Экосистема Java-приложений. Теория десктопа — Архитектура десктопных приложений.

Если вы только переходите из консольных программ в GUI, полезно читать материал слоями — сначала понять архитектуру и цикл событий, затем освоить базовые контролы, после этого перейти к компоновке и обработке событий. Такой порядок даёт прочный фундамент и снимает ощущение "случайного набора API".


Быстрый маршрут по статье

  1. Пройти первую программу на JavaFX (чеклист pom.xml, module-path, HelloApp → DemoApp).
  2. Разобраться, зачем нужен Application.launch и поток JavaFX.
  3. Освоить контейнеры компоновки (VBox, HBox, GridPane, BorderPane).
  4. Добавить обработчики через setOnAction и свойства с привязкой.
  5. Сравнить тот же сценарий на Swing (EDT, ActionListener).
  6. Закрепить на галерее Swing в Lab или мини-проектах в конце статьи.

Связанные главы: Практические примеры — Swing, Lab — окна и кнопки, Экосистема Java-приложений, JVM, память и потоки, Maven и структура проекта, Особенности десктопа.


GUI в Java

Если задача требует полноценного приложения с кнопками, полями ввода, меню и другими элементами управления, используют специализированные библиотеки для построения GUI.

Графический пользовательский интерфейс (Graphical User Interface) — система, через которую пользователь взаимодействует с программой через визуальные компоненты — окна, кнопки, списки, диаграммы. В Java доступны несколько основных направлений:

  • Swing (javax.swing) — библиотека в составе JDK; рисует "лёгкие" компоненты на Java, без нативных peer-объектов AWT.
  • JavaFX (javafx.*, проект OpenJFX) — современный стек с CSS, FXML, анимациями и аппаратным ускорением; с Java 11 подключается отдельной зависимостью.
  • AWT (java.awt) — первая графическая библиотека Java на нативных виджетах ОС; сегодня служит основой для Swing и системных диалогов.
  • SWT (Eclipse) — нативные контролы Windows/macOS/Linux; часто встречается в Eclipse IDE.
  • Compose Multiplatform (JetBrains) — декларативный UI на Kotlin; для чистого Java-стека реже, но полезен при миграции на Kotlin.
  • Vaadin — веб-интерфейс на Java без HTML/JS в коде приложения; сервер рендерит UI в браузер.

Любое GUI-приложение следует событийно-ориентированной архитектуре:

  • создаётся главное окно;
  • добавляются виджеты (элементы управления);
  • регистрируются обработчики событий (клик, ввод текста, изменение размера);
  • запускается главный цикл UI, который принимает сообщения от ОС и распределяет их по обработчикам.
JavaFX и JDK

JavaFX входил в Oracle JDK до версии 10 включительно. Начиная с Java 11 модуль вынесен в проект OpenJFX и добавляется через Maven/Gradle. Swing по-прежнему в JDK и не требует отдельных зависимостей.


Сравнение основных стеков

SwingJavaFXSWT
В составе JDKДаНет (OpenJFX)Нет
СтилизацияLook-and-Feel, UIManagerCSS, темыНативный вид ОС
Разметка UIКод + LayoutManagerКод, FXMLКод
АнимацииОграниченноTimeline, TransitionМинимально
Типичный стартУчебные проекты, legacyНовые desktop-приложенияEclipse, RCP

Для новых desktop-проектов на Java чаще выбирают JavaFX. Swing остаётся удобным первым шагом — нулевая настройка зависимостей, миллионы строк legacy-кода в enterprise, простые утилиты.


Архитектура JavaFX

JavaFX строит интерфейс как дерево узлов (Node) внутри сцены (Scene), отображаемой в окне (Stage). Это ближе к современным UI-фреймворкам (Qt, WPF), чем к раннему AWT.

КомпонентНазначение
StageГлавное (или дочернее) окно приложения
SceneКонтейнер сцены с корневым узлом и размерами
NodeЛюбой визуальный элемент — Button, Label, TextField, контейнер
ParentУзел, который может содержать дочерние (VBox, GridPane)
ApplicationТочка входа; инициализирует платформу и вызывает start(Stage)
JavaFX Application ThreadЕдинственный поток, где можно менять UI

Пример на JavaFX:

Код ITЗагрузка примера кода…

Разбор:

  • Application.launch(args) поднимает JavaFX runtime и вызывает start.
  • VBox — вертикальный контейнер; 10 — расстояние между дочерними узлами.
  • setOnAction связывает кнопку с обработчиком клика (лямбда).
  • Scene задаёт корень и размер окна; stage.show() отображает окно.

launch блокирует main до закрытия последнего окна — аналог mainloop() в Tkinter или app.exec() в Qt.


Иерархия Stage → Scene → Node

Stage (окно)
└── Scene (400×300)
└── Parent (корень, например BorderPane)
├── top: ToolBar
├── center: TableView
└── bottom: HBox с кнопками

Каждый Node имеет свойства (положение, видимость, стиль), которые можно менять из кода или привязать к другим свойствам. Изменения в UI выполняются только в JavaFX Application Thread — прямой вызов из фонового потока приведёт к IllegalStateException.


Пример простого приложения с кнопкой

Для кнопки с обработчиком используют Button и setOnAction. В отличие от Swing, здесь передаётся лямбда или метод, а не экземпляр ActionListener:

Код ITЗагрузка примера кода…

Ключевые аспекты:

  1. setOnAction — обработчик срабатывает при активации кнопки (клик или Enter при фокусе).
  2. Alert — стандартный диалог; showAndWait() блокирует до закрытия (модальный режим).
  3. launch — без него окно не появится; код после launch в main выполнится только после завершения приложения.
  4. Стили — строка CSS с префиксом -fx- (шрифт, отступы, цвета).

Инициализация приложения

Минимальный каркас:

Код ITЗагрузка примера кода…

Класс наследует Application и переопределяет start. Альтернатива без наследования — Application.launch() с анонимным подклассом, но для учебных проектов достаточно явного класса.


Подключение JavaFX через Maven

Фрагмент pom.xml для Java 17+ и OpenJFX 21:

Код ITЗагрузка примера кода…

Запуск:

mvn javafx:run

Подробнее о структуре Maven-проекта — Структуры проекта.

Типичные ловушки pom.xml (новичок видит "всё ок", сборка падает)

Симптом в IDE / компилятореЧастая причина
package javafx.application does not existВ зависимости <version>${javafx.version}</version>, но в <properties> нет <javafx.version>…</javafx.version> — Maven не резолвит артефакт
Красный ${javafx.version} в pom.xmlТо же: свойство не объявлено
invalid target release: 24 / 25maven.compiler.source/target выше установленного JDK
javafx:run молчит или не тот экранВ javafx-maven-plugin остался шаблонный mainClass (com.example…), а реальный пакет другой
Код собирается, Run падает с runtime components are missingЗапуск без module path (см. ниже) — отдельная проблема от "не скачался" OpenJFX

После любой правки pom.xmlMaven → Reload Project. Пошаговый чеклист и VM options для IDEA — Первая программа на JavaFX.


Запуск JavaFX: classpath, module-path и IDE

С Java 11 OpenJFX — отдельные модули. Для компиляции Maven кладёт JAR в classpath зависимостей, но запуск java или "голый" Run в IntelliJ без настроек часто собирает только classpath. Тогда в консоли:

Error: JavaFX runtime components are missing, and application needs to be terminated

Нужен --module-path к каталогу с платформенными JAR (javafx-controls-21-*-win.jar на Windows и т.д.) и --add-modules javafx.controls,javafx.graphics.

Три рабочих способа:

  1. mvn javafx:runjavafx-maven-plugin подставляет module path сам (удобно для проверки).
  2. Run Configuration в IDEA с VM options на $PROJECT_DIR$/target/javafx-lib после mvn compile и копирования *-win.jar (см. Первая программа на JavaFX).
  3. Сборка дистрибутива — jlink, fat-jar с плагинами; для учебных проектов обычно избыточно.
Не путать две разные ошибки

Не компилируется (package javafx… does not exist) — зависимость OpenJFX не подтянулась в проект (часто из‑за javafx.version).
Компилируется, но не стартует (runtime components are missing) — зависимость есть, но JVM запущена без module path. Исправляются разными шагами.


Поэтапное усложнение UI в одном Maven-проекте

Имеет смысл не прыгать сразу к FXML и десяткам контролов, а наращивать слои в отдельных классах Application и переключать их из одного Main:

HelloApp → одно окно: поле, кнопка, метка (VBox, setOnAction)
DemoApp → TabPane, BorderPane, привязки, ListView, ProgressBar + Timeline — [разбор в 3111](./3111.md#demoapp--галерея-контролов)
ButtonDemo → стили кнопки, Alert со стилизацией DialogPane

Так отрабатываются компоновка (контейнеры вместо координат), события (setOnAction, привязки свойств) и стили (inline CSS и классы на сцене). Каркас с Application.launch(HelloApp.class, args) в отдельном Main описан в Первая программа на JavaFX.


Контролы JavaFX

Control — интерактивный узел — кнопка, поле ввода, чекбокс. Label — статический текст или изображение.

Объект Label создаётся так:

Label label = new Label("Приветствие");
label.setStyle("-fx-text-fill: blue; -fx-background-color: white; -fx-padding: 8;");

Таблица частых свойств Label и аналогов:

Свойство / методОписаниеПример
setText(String)Текст метки"Информация"
textProperty()Свойство для привязкиlabel.textProperty().bind(...)
setFont(Font)ШрифтFont.font("Arial", FontWeight.BOLD, 14)
setWrapText(true)Перенос длинных строкдля узких панелей
setGraphic(Node)Иконка рядом с текстомImageView
setAlignment(Pos)Выравнивание внутри LabelPos.CENTER_LEFT
setMaxWidth(Double.MAX_VALUE)Растягивание по ширине контейнерав VBox с setFillWidth(true)

Методы, общие для всех Node:

МетодОписание
setVisible(boolean)Показать или скрыть
setDisable(boolean)Отключить ввод, "серый" вид
setOnMouseClicked(...)Обработчик клика мыши
getStyleClass().add("...")CSS-класс из таблицы стилей

Пример оформленных меток:

Код ITЗагрузка примера кода…


Динамическое обновление текста и привязки

Прямой вызов setText работает, но для связи нескольких виджетов удобнее properties и bindings:

Код ITЗагрузка примера кода…

textProperty().bind(...) автоматически обновляет метку при каждом изменении поля — без ручного setText в обработчике.


Контейнеры компоновки

Размещение узлов на сцене задают layout-контейнеры (Pane и наследники). В одном родителе используют один тип менеджера; смешивать произвольно нельзя (дочерний узел принадлежит одному Parent).

VBox — вертикальный поток:

VBox box = new VBox(8); // 8 px между детьми
box.getChildren().addAll(label, field, button);
box.setAlignment(Pos.CENTER);
VBox.setVgrow(table, Priority.ALWAYS); // таблица забирает свободное место

HBox — горизонтальный поток; параметры spacing, alignment, HBox.setHgrow.

GridPane — сетка строк и столбцов:

МетодОписание
add(node, column, row)Ячейка сетки
add(node, col, row, colspan, rowspan)Объединение ячеек
setHgap / setVgapРасстояние между ячейками
GridPane.setConstraints(node, ...)Прилипание к краям (GridPane.setHgrow)
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(8);
grid.add(new Label("Имя:"), 0, 0);
grid.add(nameField, 1, 0);
grid.add(new Label("Пароль:"), 0, 1);
grid.add(passwordField, 1, 1);

BorderPane — зоны top, bottom, left, right, center (классическая форма с меню и центральной областью):

BorderPane root = new BorderPane();
root.setTop(toolBar);
root.setCenter(tableView);
root.setBottom(statusBar);

StackPane — наложение слоёв (кнопка по центру, фон, оверлей загрузки).

AnchorPane, FlowPane, TilePane — для специальных случаев; в формах чаще GridPane или VBox/HBox.

Позиционирование: чего не ждать от JavaFX

  • У обычных форм нет "свободного" размещения по пикселям на Scene — только менеджеры внутри Parent.
  • VBox по умолчанию тянет детей к началу (часто слева сверху); центрирование — setAlignment(Pos.CENTER).
  • Чтобы поле или кнопка растянулись по ширине, недостаточно одного setPrefWidth: часто нужны setMaxWidth(Double.MAX_VALUE) и VBox.setVgrow(node, Priority.ALWAYS).
  • Сложный экран = вложенные панели (BorderPane с VBox в center, HBox для кнопок), а не один контейнер на всё.

Таблица "ожидание → решение" для новичка — в Первая программа на JavaFX.


Обработка событий

JavaFX следует event-driven модели. Платформа получает сообщения от ОС, ставит их в очередь и вызывает зарегистрированные обработчики в UI-потоке.

Основные способы подписки:

button.setOnAction(e -> { /* клик / Enter */ });

node.setOnMouseClicked(e -> {
double x = e.getX();
double y = e.getY();
});

field.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER) submit();
});

stage.widthProperty().addListener((obs, oldVal, newVal) -> resizeContent());

Для фильтрации и всплытия есть цепочка event filter (сверху вниз) и event handler (снизу вверх). Для большинства учебных задач достаточно setOnAction и setOnMouseClicked.

Controls (кнопки, поля) генерируют высокоуровневые события; для низкоуровневого ввода — MouseEvent, KeyEvent, ScrollEvent.


FXML и контроллер

Интерфейс можно описать в FXML (XML-разметка) и связать с Java-классом controller:

hello.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.HelloController"
spacing="10" style="-fx-padding: 20;">
<Label text="Конвертер °C → °F"/>
<TextField fx:id="celsiusField" promptText="Температура °C"/>
<Button text="Перевести" onAction="#convert"/>
<Label fx:id="resultLabel"/>
</VBox>

HelloController.java:

Код ITЗагрузка примера кода…

Загрузка в Application.start:


FXMLLoader loader = new FXMLLoader(getClass().getResource("/hello.fxml"));
Parent root = loader.load();
stage.setScene(new Scene(root, 360, 200));

Такой подход отделяет разметку от логики — аналог .ui в Qt Designer или отдельного ui.py в Tkinter-проектах.


CSS-стилизация

JavaFX понимает CSS с префиксом -fx-. Стили задают inline (setStyle), через файл styles.css или классы:

scene.getStylesheets().add(getClass().getResource("/app.css").toExternalForm());
.button-primary {
-fx-background-color: #2563eb;
-fx-text-fill: white;
-fx-font-weight: bold;
}
.error-label {
-fx-text-fill: #dc2626;
}

Тема Modena включена по умолчанию; для кастомного вида переопределяют селекторы .root, .button, .text-field.

Стилизация окна и диалогов

Что стилизуетеКак
Корень сцены, кнопки, поляnode.setStyle("…") или классы + scene.getStylesheets()
Вся сцена сразуСелектор .root в CSS-файле
Alertalert.getDialogPane() — тот же CSS, плюс класс на pane (.custom-alert .header-panel, .button-bar .button)
"Как нативный Windows 11"Нет — Alert рисуется JavaFX, не системный MessageBox

DialogPane у Alert — обычный узел: можно задать тёмный фон, красную шапку и hover на кнопке OK. Иконка типа alert меняется ограниченно; для произвольной вёрстки и анимаций используют Dialog<T> с кастомным content. Пример с красной кнопкой и стилизованным диалогом — Первая программа на JavaFX.


Swing — встроенный GUI JDK

Swing (javax.swing) рисует компоненты на Java поверх AWT. Архитектура окна:

JFrame (главное окно)
└── contentPane
├── JLabel, JButton, JTextField
└── JPanel + LayoutManager (FlowLayout, BorderLayout, GridLayout, BoxLayout)

Графический интерфейс Swing работает в Event Dispatch Thread (EDT). Создание и изменение компонентов выполняют через SwingUtilities.invokeLater():

Код ITЗагрузка примера кода…

Обработка клика — ActionListener (или лямбда):


JButton button = new JButton("Нажми меня");
button.addActionListener(e -> JOptionPane.showMessageDialog(frame, "Клик!"));

Полные примеры счётчика, формы входа, меню и калькулятора с построчным разборомLab — Java Swing; краткие фрагменты — Практические примеры — Swing.


Геометрические менеджеры Swing

МенеджерПоведениеКогда использовать
FlowLayoutЭлементы в ряд, перенос строкПростые панели кнопок
BorderLayoutNORTH, SOUTH, EAST, WEST, CENTERКлассическое окно с toolbar и центром
GridLayoutРавномерная сеткаФормы с одинаковыми ячейками
GridBagLayoutГибкая сетка с весамиСложные формы
BoxLayoutВертикальный или горизонтальный стекСписки полей

Важно: внутри одного контейнера нельзя смешивать менеджеры — у каждого JPanel один LayoutManager.

Пример BorderLayout:

JPanel panel = new JPanel(new BorderLayout(8, 8));
panel.add(toolbar, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
panel.add(statusLabel, BorderLayout.SOUTH);

Swing и потоки

Swing, как и JavaFX, не потокобезопасен. Обновление UI из фонового потока:

  • SwingUtilities.invokeLater(() -> label.setText("Готово"));
  • SwingWorker — для длительных задач с прогрессом и публикацией результата в EDT.

В JavaFX аналог — Platform.runLater(...) и класс Task / Service.

Пример безопасного обновления из фонового потока (JavaFX):


new Thread(() -> {
String result = loadFromNetwork(); // долго
Platform.runLater(() -> statusLabel.setText(result));
}).start();

Для сетевых и файловых операций в GUI сверяйтесь с работой с БД и общими принципами многопоточности.


AWT — кратко

AWT (java.awt) использует peer-объекты — нативные виджеты Windows, macOS или X11. Внешний вид зависит от ОС; кастомизация ограничена. Сегодня AWT применяют там, где Swing/JavaFX опираются на низкий уровень — системный трей, некоторые диалоги, работа с Graphics, Robot, буфер обмена. Новые экранные формы на чистом AWT писать редко — разумнее Swing или JavaFX.


Организация кода в реальном приложении

В учебных примерах всё в одном классе; в утилитах удобнее разделить ответственность:

  • MainApp / Application — запуск, конфигурация Stage;
  • view — FXML или фабрики панелей;
  • controller — обработчики событий, связь view и model;
  • service — логика без UI (файлы, HTTP, БД);
  • model — данные и правила предметной области.

Такой каркас упрощает тестирование сервисов без поднятия UI и повторное использование логики в CLI или REST.

// MainApp.java — только сборка
public class MainApp extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"));
Parent root = loader.load();
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) { launch(args); }
}

Частые анти-паттерны

  • Долгая операция прямо в setOnAction / ActionListener без Task, SwingWorker или фонового потока — UI "замирает".
  • Обновление виджетов из worker-потока без Platform.runLater / invokeLater.
  • Глобальные статические ссылки на все контролы вместо явной структуры приложения.
  • Смешивание нескольких layout-менеджеров на одном JPanel без вложенных панелей.
  • Отсутствие валидации ввода перед парсингом Double.parseDouble и SQL. См. Проверка и валидация.
  • Игнорирование setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) — процесс JVM остаётся висеть.

Примеры реализации

Таймер обратного отсчёта (JavaFX)

Timeline планирует периодические обновления в UI-потоке:

Код ITЗагрузка примера кода…


Форма входа с валидацией (JavaFX)

Код ITЗагрузка примера кода…


Таблица и меню (Swing)

Для data-heavy UI в legacy-стеке часто используют JTable + TableModel:

Код ITЗагрузка примера кода…

В JavaFX аналог — TableView с ObservableList и колонками PropertyValueFactory.


Частые ошибки

СимптомПричина
package javafx… does not exist при компиляцииНет <javafx.version> в properties или Maven не Reload
Окно не появляетсяНет launch() / setVisible(true)
IllegalStateException: Not on FX application threadUI меняют из фонового потока без Platform.runLater
Module javafx.controls not foundOpenJFX не добавлен в module path / Maven
JavaFX runtime components are missingЗапуск без --module-path (IDEA Run без VM options)
invalid target releaseВерсия в pom выше установленного JDK
JVM не завершается после закрытия окна SwingНет EXIT_ON_CLOSE или остались не-daemon потоки
UI зависаетБлокирующий код в обработчике кнопки
FXML не находит controllerНеверный fx:controller или ресурс не в resources
Элементы "слиплись" слеваНет padding/spacing/alignment у VBox/HBox

Пошаговый разбор "Maven vs module-path" — Первая программа на JavaFX.


Что попробовать

  1. Тот же сценарий "приветствие" на Swing и JavaFX — сравните EDT и JavaFX thread.
  2. Вынести разметку в FXML и подключить CSS-файл.
  3. Долгую задачу обернуть в Task с индикатором ProgressIndicator.
  4. Собрать JAR с javafx-maven-plugin и запустить на другой машине с JRE 17+.
  5. Общую теорию окон и mainloop — Архитектура десктопных приложений.

Практика и справочник


Сравнение с Python

Параллель в экосистеме Python — Tkinter и GUI и галерея Tkinter в Lab — там mainloop и Tcl/Tk, здесь JavaFX Application Thread и OpenJFX, для Swing без Maven — Lab — 1143. Оба материала опираются на одну и ту же теорию десктопа.