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".
Быстрый маршрут по статье
- Пройти первую программу на JavaFX (чеклист
pom.xml, module-path, HelloApp → DemoApp). - Разобраться, зачем нужен
Application.launchи поток JavaFX. - Освоить контейнеры компоновки (
VBox,HBox,GridPane,BorderPane). - Добавить обработчики через
setOnActionи свойства с привязкой. - Сравнить тот же сценарий на Swing (EDT,
ActionListener). - Закрепить на галерее 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 входил в Oracle JDK до версии 10 включительно. Начиная с Java 11 модуль вынесен в проект OpenJFX и добавляется через Maven/Gradle. Swing по-прежнему в JDK и не требует отдельных зависимостей.
Сравнение основных стеков
| Swing | JavaFX | SWT | |
|---|---|---|---|
| В составе JDK | Да | Нет (OpenJFX) | Нет |
| Стилизация | Look-and-Feel, UIManager | CSS, темы | Нативный вид ОС |
| Разметка 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Загрузка примера кода…
Ключевые аспекты:
setOnAction— обработчик срабатывает при активации кнопки (клик или Enter при фокусе).Alert— стандартный диалог;showAndWait()блокирует до закрытия (модальный режим).launch— без него окно не появится; код послеlaunchвmainвыполнится только после завершения приложения.- Стили — строка 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 / 25 | maven.compiler.source/target выше установленного JDK |
javafx:run молчит или не тот экран | В javafx-maven-plugin остался шаблонный mainClass (com.example…), а реальный пакет другой |
Код собирается, Run падает с runtime components are missing | Запуск без module path (см. ниже) — отдельная проблема от "не скачался" OpenJFX |
После любой правки pom.xml — Maven → 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.
Три рабочих способа:
mvn javafx:run—javafx-maven-pluginподставляет module path сам (удобно для проверки).- Run Configuration в IDEA с VM options на
$PROJECT_DIR$/target/javafx-libпослеmvn compileи копирования*-win.jar(см. Первая программа на JavaFX). - Сборка дистрибутива — 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) | Выравнивание внутри Label | Pos.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-файле |
Alert | alert.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 | Элементы в ряд, перенос строк | Простые панели кнопок |
BorderLayout | NORTH, 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 thread | UI меняют из фонового потока без Platform.runLater |
Module javafx.controls not found | OpenJFX не добавлен в 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.
Что попробовать
- Тот же сценарий "приветствие" на Swing и JavaFX — сравните EDT и JavaFX thread.
- Вынести разметку в FXML и подключить CSS-файл.
- Долгую задачу обернуть в
Taskс индикаторомProgressIndicator. - Собрать JAR с
javafx-maven-pluginи запустить на другой машине с JRE 17+. - Общую теорию окон и mainloop — Архитектура десктопных приложений.
Практика и справочник
- Первая программа на JavaFX
- Справочник JavaFX и Swing — элементы UI
- Практические примеры — Swing
- Lab — Java Swing — окна и кнопки — галерея для лабораторной и курсовой
- Экосистема Java-приложений — UI
- Maven и JavaFX
- JVM и потоки
- Архитектура десктопных приложений · раздел "Десктопные приложения" · Особенности разработки десктопных приложений
Параллель в экосистеме Python — Tkinter и GUI и галерея Tkinter в Lab — там mainloop и Tcl/Tk, здесь JavaFX Application Thread и OpenJFX, для Swing без Maven — Lab — 1143. Оба материала опираются на одну и ту же теорию десктопа.