Первая программа на JavaFX
Первая программа на JavaFX
Где применяют JavaFX
JavaFX (OpenJFX) — рекомендуемый стек для новых desktop-приложений на Java — CSS, FXML, анимации, TableView, диалоги. С Java 11 библиотека подключается через Maven или Gradle; в JDK "из коробки" её уже нет.
Мы соберём "Конвертер °C → °F" и разберём Application.launch() — без него окно не останется на экране, как mainloop() в Tkinter или SwingUtilities.invokeLater + видимое окно в Swing.
Теория: JavaFX и GUI. Элементы UI по рецептам: Справочник по JavaFX и Swing — элементы UI. Swing без Maven — Lab — окна и кнопки (построчный разбор для лабораторной) или кратко в Простые приложения на Java — Swing.
Что получится
Поле ввода, кнопки "Перевести" и "Выход", метка результата; Enter в поле вызывает расчёт.
Подготовка проекта (Maven)
Минимальный pom.xml для Java 17 и OpenJFX 21:
Код ITЗагрузка примера кода…
Запуск из каталога проекта:
mvn javafx:run
Подробнее о структуре каталогов — Структуры проекта.
Чаще всего Maven не скачивает OpenJFX, потому что в <version>${javafx.version}</version> нет свойства javafx.version в блоке <properties> — в IDEA плейсхолдер подсвечивается красным. Без артефакта javafx-controls компилятор честно пишет package javafx.application does not exist. Это незаданная версия зависимости.
Чеклист pom.xml перед первым Run
| Проверка | Должно быть |
|---|---|
| Свойство версии | <javafx.version>21.0.7</javafx.version> (или другая стабильная 21.x) |
| Зависимость | org.openjfx:javafx-controls с той же версией, без красного ${javafx.version} |
| Версия Java | maven.compiler.source / target (или release) совпадают с Project SDK в IDEA — не 25, если установлен JDK 21/24 |
mainClass в javafx-maven-plugin | Реальный класс с public static void main, например ru.example.Main, а не шаблон com.example.app.Main |
| После правок | Правый клик по pom.xml → Maven → Reload Project |
С JDK 11+ JavaFX нет в стандартном JDK (в Oracle JDK до 10 он был внутри). Фраза "Java установлена" не означает "JavaFX доступен" — нужны зависимости OpenJFX и корректный запуск.
Две точки входа — Main и класс Application
В учебных Maven-проектах удобно разделить роли:
HelloApp extends Application— только UI —start(Stage), виджеты, обработчики;Main— одна строка запуска, какой экран открыть.
package ru.example;
import javafx.application.Application;
public class Main {
public static void main(String[] args) {
Application.launch(HelloApp.class, args);
// Application.launch(DemoApp.class, args);
}
}
Переключение между "приветствием" и большим демо — комментарием одной строки, без копирования launch в каждый класс. Альтернатива: public static void main прямо в HelloApp с вызовом launch(args) — тогда Run в IDEA можно повесить на этот класс.
В javafx-maven-plugin укажите тот класс, у которого есть main (часто это Main, а не HelloApp).
Запуск — Maven, IDEA и module-path
Через Maven (плагин сам подставляет module path):
Maven → Plugins → javafx → javafx:run
Через Run в IntelliJ IDEA после Java 11 недостаточно "просто classpath": без module path появляется:
Error: JavaFX runtime components are missing, and application needs to be terminated
В логе видно, что JAR JavaFX лежат на classpath, а runtime ждёт --module-path с платформенными JAR (javafx-controls-*-win.jar на Windows).
Минимальные VM options для конфигурации Application (после сборки проекта):
--module-path "$PROJECT_DIR$/target/javafx-lib"
--add-modules javafx.controls,javafx.graphics
--enable-native-access=javafx.graphics
Чтобы папка target/javafx-lib появилась при mvn compile, в pom.xml можно добавить копирование нативных JAR OpenJFX (классификатор win / linux / mac под вашу ОС):
Код ITЗагрузка примера кода…
Порядок в IDEA: Build Project (или mvn compile) → выбрать сохранённую конфигурацию Main / DemoApp с VM options → Run. Временная конфигурация "Run Main.java" без VM options снова даст runtime components are missing.
Предупреждения JDK 24 про sun.misc.Unsafe из JavaFX в консоли обычно не мешают работе окна — их можно игнорировать на этапе обучения.
Поэтапное расширение одного проекта
Логичный маршрут "от нуля к наглядной галерее" (как в учебном HelloApp):
| Шаг | Класс | Что добавляете |
|---|---|---|
| 1 | HelloApp | TextField, Button, Label, VBox, setOnAction — одно окно 320×180 |
| 2 | DemoApp | TabPane (формы, список, прогресс), BorderPane, Slider + привязка к label, Timeline, строка статуса — подробный разбор |
| 3 | ButtonDemo | StackPane, стили кнопки, Alert со стилизацией через getDialogPane() |
На шаге 2 полезно увидеть привязку свойств вместо ручного setText в каждом обработчике:
volumeLabel.textProperty().bind(
Bindings.format("Громкость: %.0f%%", volumeSlider.valueProperty())
);
Стили можно подключить inline (setStyle), через CSS-файл в resources или строкой в scene.getStylesheets().add("data:text/css,...") — для демо без отдельного файла.
Минимальное окно
Код ITЗагрузка примера кода…
Разбор:
Application— точка входа JavaFX; платформа вызываетstart(Stage).Stage— окно;Scene— содержимое с корневым узлом и размером.launch(args)запускает UI-поток и цикл событий; безshow()окно не видно.
Конвертер температуры
Код ITЗагрузка примера кода…
Разбор:
convert()читает текст поля, парсит число и обновляетresultLabel.NumberFormatExceptionперехватывает нечисловой ввод;Alertпоказывает сообщение об ошибке.GridPaneвыравнивает метки и поля в сетке;HBoxгруппирует кнопки в ряд.setOnActionиsetOnKeyPressedсKeyCode.ENTER— два способа вызвать одну логику.
DemoApp — галерея контролов
DemoApp — второй учебный экран после конвертера — три мини-сцены во вкладках, общая строка состояния и стили через CSS-классы. Так отрабатываются контейнеры, привязки свойств и анимация в одном окне — без FXML и без отдельного окна на каждый пример.
Переключение в Main:
Application.launch(DemoApp.class, args);
// Application.launch(HelloApp.class, args);
Исходник держите рядом с HelloApp (например, ru.timur.crm.DemoApp). Ниже — как добавляется каждый блок, а не копия всего файла целиком.
Связанные материалы
- Теория GUI и выбор стека — JavaFX и GUI, раздел "Десктопные приложения"
- Рецепты по каждому контролу — справочник 3112
- UI-поток и почему нельзя блокировать окно — Особенности разработки десктопных приложений, JVM и потоки
Зачем отдельный класс после конвертера
Конвертер учит одну форму и обработку ошибок через Alert. DemoApp показывает, как в реальном приложении собирают экран:
- несколько зон (шапка, центр, статус) вместо одного
GridPane; - вкладки вместо отдельных окон;
- общий статус — один
Label, на который ссылаются все вкладки; - привязки (
bind) — UI обновляется сам, без ручногоsetTextна каждый тик слайдера; - анимация (
Timeline) — прогресс безThread.sleepв обработчике кнопки.
Это шаг 2 в поэтапном расширении; шаг 3 — ButtonDemo со стилями и диалогами.
Каркас — BorderPane, заголовок, вкладки и статус
BorderPane — контейнер с пятью зонами (top, bottom, left, right, center). Подходит для классической раскладки "шапка — содержимое — статус", описанной в архитектуре десктопа.
TabPane — панель вкладок; каждая Tab хранит свой корневой Node (форму, список, прогресс).
Строка состояния (status bar) — обычно Label внизу окна; показывает подсказки и результат последнего действия. В DemoApp один statusLabel передаётся в formTab, listTab и progressTab, чтобы не дублировать виджет.
Код ITЗагрузка примера кода…
Разбор по строкам
statusLabel.getStyleClass().add("status-bar")— класс из CSS, не inline-стиль; оформление в константеSTYLESHEET.new TabPane(formTab(...), ...)— varargs: три вкладки создаются фабричными методами; каждый получает ту же ссылку наstatus.TabClosingPolicy.UNAVAILABLE— у вкладок нет крестика закрытия; для учебного демо проще, чем восстанавливать закрытую вкладку.VBox(4, ...)— вертикальный стек с промежутком 4 px между заголовком и подзаголовком.BorderPane.setMargin(tabs, ...)— отступы только у центра; шапка и статус на всю ширину.
| Узел | Роль |
|---|---|
statusLabel | Единая обратная связь с пользователем; вкладки вызывают status.setText(...) |
TabPane | Три экрана в одном Stage без new Stage() |
VBox header | Статичный заголовок вне вкладок |
BorderPane.setMargin | "Воздух" по бокам у блока вкладок |
setMaxWidth(Double.MAX_VALUE) у статуса заставляет Label растянуться на всю ширину — иначе метка остаётся узкой и "плавает" слева. Подробнее про растягивание — ниже и в Справочник по JavaFX и Swing — элементы UI — StatusBar.
Стили и CSS-классы без отдельного файла
В JavaFX стили — CSS с префиксом -fx- (обзор в JavaFX и GUI). Три способа подключения:
- inline —
node.setStyle("..."); - файл —
scene.getStylesheets().add(url); - data-URL — CSS-строка прямо в коде (удобно для лабораторной без
resources).
В DemoApp стили в text block и подключаются так:
private static final String STYLESHEET = """
.root { -fx-font-family: "Segoe UI", system-ui, sans-serif; … }
.card { -fx-background-color: white; -fx-border-radius: 10; … }
""";
scene.getStylesheets().add("data:text/css," + STYLESHEET.replace("\n", ""));
.root— корень сцены; шрифт и фон всего окна..card,.header-title,.status-bar— классы на узлах черезgetStyleClass().add(...).replace("\n", "")— data-URL не любит переводы строк в одной строке запроса.
Вспомогательные методы убирают повторение:
private VBox card(Node content) {
VBox box = new VBox(content);
box.getStyleClass().add("card");
VBox.setVgrow(content, Priority.ALWAYS);
return box;
}
private Label styledLabel(String text, String styleClass) {
Label label = new Label(text);
label.getStyleClass().add(styleClass);
return label;
}
card()— обёртка "белая карточка с рамкой" вокруг содержимого вкладки.VBox.setVgrow(content, Priority.ALWAYS)— внутренний блок забирает свободную высоту карточки (важно дляListView).
Для продакшена чаще кладут app.css в src/main/resources — см. стилизацию ниже.
Вкладка "Формы"
Метод formTab(Label status) — типичная экранная форма: подпись слева, контрол справа. Контейнер — GridPane (сетка строк и столбцов), как в конвертере, но с большим числом типов ввода.
TextField и PasswordField
TextField — однострочный ввод текста. PasswordField — тот же API, но символы маскируются (наследник TextField).
TextField nameField = new TextField();
nameField.setPromptText("Имя");
nameField.setMaxWidth(Double.MAX_VALUE);
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("Пароль");
passwordField.setMaxWidth(Double.MAX_VALUE);
setPromptText— подсказка-серый placeholder, пока поле пустое; не то же самое, что значение по умолчанию.setMaxWidth(Double.MAX_VALUE)+GridPane.setHgrow(..., Priority.ALWAYS)— поле тянется на ширину ячейки, а не по числу символов.
Чтение при отправке: nameField.getText(), passwordField.getText(); пустое имя в DemoApp заменяют на "(без имени)" через isBlank().
ComboBox
ComboBox — выпадающий список фиксированных вариантов (роль пользователя, категория, статус).
ComboBox<String> roleBox = new ComboBox<>();
roleBox.getItems().addAll("Гость", "Пользователь", "Администратор");
roleBox.setValue("Пользователь");
getItems()— изменяемый список (ObservableList); можноadd/removeв runtime.getValue()— выбранный элемент; для нередактируемого списка надёжнее, чемgetText().setValueдо первого показа — начальное выделение без клика пользователя.
Рецепт — Справочник по JavaFX и Swing — элементы UI — ComboBox.
DatePicker
DatePicker — календарь для выбора даты; значение типа LocalDate (java.time), не Date.
DatePicker datePicker = new DatePicker(LocalDate.now());
В обработчике "Применить" дату превращают в строку для статуса:
String date = datePicker.getValue() != null
? datePicker.getValue().format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))
: "—";
getValue()может бытьnull, если пользователь очистил поле — проверка обязательна.- Форматирование — java.time в справочнике; обзор типов даты — Ключевые классы и интерфейсы стандартной библиотеки.
Slider и привязка свойств (Bindings)
Slider — ползунок в числовом диапазоне. Привязка (binding) — связь двух свойств JavaFX: при изменении источника цель обновляется сама, без явного setText в коде.
Slider volumeSlider = new Slider(0, 100, 40);
volumeSlider.setShowTickLabels(true);
volumeSlider.setShowTickMarks(true);
Label volumeLabel = new Label();
volumeLabel.textProperty().bind(
Bindings.format("Громкость: %.0f%%", volumeSlider.valueProperty())
);
new Slider(min, max, value)— диапазон 0–100, старт 40.valueProperty()—ObservableValue<Double>; источник для привязки.Bindings.format(...)— какString.format, но реактивно: метка всегда синхронна со слайдером.
Это контраст с императивным стилем в конвертере, где resultLabel.setText(...) вызывают вручную в convert(). Оба подхода допустимы: привязки — для "зеркала" UI; обработчики — для бизнес-логики по кнопке.
CheckBox и RadioButton
- CheckBox — независимый флаг (вкл/выкл уведомления).
- RadioButton — один вариант из группы; без ToggleGroup можно выбрать несколько кнопок сразу — частая ошибка новичка.
CheckBox notifyCheck = new CheckBox("Уведомления");
notifyCheck.setSelected(true);
ToggleGroup themeGroup = new ToggleGroup();
RadioButton lightTheme = new RadioButton("Светлая");
RadioButton darkTheme = new RadioButton("Тёмная");
lightTheme.setToggleGroup(themeGroup);
darkTheme.setToggleGroup(themeGroup);
lightTheme.setSelected(true);
isSelected()у чекбокса — boolean для строки статуса ("вкл" / "выкл").lightTheme.isSelected()— какая тема выбрана в группе из двух.
Кнопка "Применить" и сбор данных формы
Кнопка по умолчанию (setDefaultButton(true)) — нажатие Enter в форме активирует её, пока фокус в контроле на сцене.
Код ITЗагрузка примера кода…
Разбор обработчика
- Один
setOnActionсобирает снимок всех контролов — паттерн "Прочитать форму → показать в статусе" (позже тот же сбор уйдёт в сохранение или API). status.setText— запись в общую строку состояния, переданную параметромstatus.- Тема и уведомления — разные модели: флаг vs взаимоисключающий выбор.
Размещение в GridPane
GridPane grid = new GridPane(12, 10);
grid.add(new Label("Имя:"), 0, 0);
grid.add(nameField, 1, 0);
grid.add(volumeLabel, 0, 4, 2, 1);
grid.add(new HBox(12, lightTheme, darkTheme), 1, 7);
grid.add(applyButton, 1, 8);
GridPane.setHgrow(nameField, Priority.ALWAYS);
GridPane.setHgrow(passwordField, Priority.ALWAYS);
GridPane.setHgrow(roleBox, Priority.ALWAYS);
return new Tab("Формы", card(grid));
GridPane(12, 10)—hgap12 иvgap10 между ячейками.grid.add(node, col, row, colspan, rowspan)—volumeLabelи слайдер на две колонки.HBox(12, lightTheme, darkTheme)— горизонтальный ряд радиокнопок с зазором 12 px.return new Tab("Формы", card(grid))— вкладка = заголовок + содержимое в "карточке".
Вкладка "Список"
Здесь модель данных — список строк в памяти (ListView), без БД. Паттерн CRUD в миниатюре — добавить, удалить, выделить.
ListView показывает ObservableList; изменения списка сразу видны на экране.
ListView<String> listView = new ListView<>();
listView.getItems().addAll("Java", "Kotlin", "Scala", "Groovy");
listView.getSelectionModel().selectFirst();
getItems()— живая коллекция;add/remove/clearобновляют UI.getSelectionModel()— какая строка выделена; нужна и для удаления, и для подсветки в статусе.
Добавление (с проверкой пустоты и дубликата):
addButton.setOnAction(e -> {
String text = itemField.getText().trim();
if (!text.isEmpty() && !listView.getItems().contains(text)) {
listView.getItems().add(text);
listView.getSelectionModel().select(text);
itemField.clear();
status.setText("Добавлено: " + text);
}
});
Удаление выбранного элемента:
removeButton.setOnAction(e -> {
String selected = listView.getSelectionModel().getSelectedItem();
if (selected != null) {
listView.getItems().remove(selected);
status.setText("Удалено: " + selected);
}
});
Слушатель выделения — реакция на клик по строке, без отдельной кнопки "Показать":
listView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
if (newVal != null) {
status.setText("Выбрано: " + newVal);
}
});
selectedItemProperty()— свойство JavaFX;addListener— подписка на смену выделения (обработка событий в JavaFX и GUI).- Проверка
newVal != null— после удаления последнего элемента выделение может стать пустым.
Компоновка вкладки
VBox— список сверху, панель действий снизу.VBox.setVgrow(listView, Priority.ALWAYS)— список растягивается по высоте.HBox(8, itemField, addButton, removeButton)— поле и кнопки в один ряд.HBox.setHgrow(itemField, Priority.ALWAYS)— поле "Новый элемент" на всю ширину ряда.
Вкладка "Прогресс"
Вкладка связывает индикатор прогресса, ручной слайдер и анимацию по таймеру. Главное правило: долгая работа не должна выполняться в setOnAction — иначе окно "зависнет" (Особенности разработки десктопных приложений). Timeline крутит кадры в JavaFX Application Thread, не блокируя очередь событий надолго.
ProgressBar хранит прогресс как дробь 0.0 … 1.0, не проценты:
ProgressBar progressBar = new ProgressBar(0);
progressBar.setMaxWidth(Double.MAX_VALUE);
Слайдер 0…1 синхронизирует полосу и подпись:
manualSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
progressBar.setProgress(newVal.doubleValue());
percentLabel.setText(String.format("%.0f%%", newVal.doubleValue() * 100));
});
setProgress(0.42)— 42% на полосе.- Один слушатель обслуживает и ручное движение, и анимацию — не дублируйте логику в
Timeline.
Имитация загрузки через Timeline
Код ITЗагрузка примера кода…
Разбор анимации
KeyFrame(Duration.ZERO, …)— мгновенный сброс слайдера в 0 перед стартом.KeyFrame(Duration.seconds(2.5), new KeyValue(..., 1))— за 2,5 с плавно довестиvaluePropertyдо 1.setOnFinished— вернуть кнопки и слайдер после конца; без этого UI останется отключённым.setDisable(true)на время — пользователь не сбивает анимацию вторым кликом.
Импорты: javafx.animation.Timeline, KeyFrame, KeyValue, javafx.util.Duration. Подробнее — Timeline в справочнике JavaFX и таймер в JavaFX и GUI.
Для реальной загрузки из сети или диска используют Task + Platform.runLater, а не Timeline — см. JavaFX и GUI — потоки и UI.
Сводка по вкладкам
| Вкладка | Контролы и приёмы | Чему учит |
|---|---|---|
| "Формы" | TextField, PasswordField, ComboBox, DatePicker, Slider, CheckBox, RadioButton, GridPane, Bindings | Ввод и выбор; форма "метка | поле"; привязка свойств |
| "Список" | ListView, TextField, кнопки, selectedItemProperty | Модель в памяти; add/remove/select; слушатель свойства |
| "Прогресс" | ProgressBar, Slider, Timeline, KeyFrame / KeyValue | Диапазон 0–1; анимация без блокировки UI |
Общее для всего DemoApp
BorderPane— каркас окна;TabPane— несколько экранов в одномStage;- один
statusLabel— обратная связь; - CSS-классы и
card()— единый визуальный стиль; - фабрики
formTab/listTab/progressTab— разбиениеstart()на читаемые части (при росте проекта то же делают с FXML).
- Рецепты по каждому контролу — справочник 3112 (DatePicker, TabPane, ListView, ProgressBar, Slider, Timeline).
- Стили кнопки и
Alert— шаг 3, классButtonDemo, и стилизация ниже. - Теория десктопа и UI-поток — архитектура, 112.md.
- Маршрут Java-раздела — ветка "Десктоп (JavaFX / Swing)" в intro раздела Java.
GridPane и VBox
GridPane— таблица строк и столбцов; удобен для форм "метка | поле".VBox/HBox— вертикальный или горизонтальный стек элементов.
В одном Parent у каждого дочернего узла одна позиция; для сложных форм вкладывают панели друг в друга.
Позиционирование и компоновка — что путает новичка
JavaFX не расставляет виджеты "как в HTML по умолчанию" и не использует абсолютные x/y на сцене в обычных формах. Положение задаёт контейнер (VBox, HBox, GridPane, BorderPane, StackPane):
| Ожидание | Как в JavaFX |
|---|---|
| "Кнопка по центру окна" | StackPane + кнопка, или VBox/HBox с setAlignment(Pos.CENTER) |
| "Поле на всю ширину" | setMaxWidth(Double.MAX_VALUE) у поля и VBox.setVgrow(field, Priority.ALWAYS) при необходимости |
| "Меню сверху, таблица в центре, статус снизу" | BorderPane: setTop / setCenter / setBottom |
| "Вкладки с разным содержимым" | TabPane, каждая Tab со своим корнем (VBox, GridPane…) |
Типичная ошибка — положить всё в VBox без отступов и выравнивания: элементы "прилипают" к левому краю. Помогают setPadding(new Insets(20)), setSpacing(10), setAlignment(Pos.CENTER).
Один узел нельзя одновременно быть в двух родителях. Для сложного экрана вкладывают панели — внешний BorderPane, внутри центра — VBox с формой, рядом — HBox с кнопками.
Стилизация — окно, кнопка и Alert
Inline (быстро для эксперимента):
button.setStyle("-fx-background-color: #e53935; -fx-text-fill: white; -fx-padding: 10 20;");
root.setStyle("-fx-padding: 20; -fx-background-color: #2d3436;");
CSS-классы на сцене (удобнее для нескольких виджетов):
button.getStyleClass().add("primary-button");
scene.getStylesheets().add(getClass().getResource("/app.css").toExternalForm());
Alert — не системный MessageBox Windows — внешний вид ограничен темой JavaFX, но шапку, текст и кнопки можно переопределить через DialogPane:
Alert alert = new Alert(Alert.AlertType.INFORMATION);
DialogPane pane = alert.getDialogPane();
pane.getStylesheets().add("data:text/css," + css.replace("\n", ""));
pane.getStyleClass().add("custom-alert");
alert.showAndWait();
Селекторы вроде .custom-alert .header-panel и .button-bar .button меняют фон диалога и кнопку OK; иконку типа (INFORMATION, ERROR) полностью "перекрасить" сложнее — для полного контроля используют Dialog<> с кастомным содержимым (JavaFX и GUI — CSS и диалоги).
Диалоги и меню
Выбор файла:
import javafx.stage.FileChooser;
FileChooser chooser = new FileChooser();
chooser.setTitle("Выберите файл");
chooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("Текст", "*.txt")
);
java.io.File file = chooser.showOpenDialog(stage);
if (file != null) {
// чтение file
}
Меню:
Код ITЗагрузка примера кода…
Частые ошибки
| Симптом | Причина | Что сделать |
|---|---|---|
package javafx.application does not exist | Нет свойства javafx.version или Maven не перезагружен | Добавить <javafx.version>…</javafx.version>, Reload Project |
${javafx.version} красный в IDEA | Свойство не объявлено | См. чеклист выше |
invalid target release: 24 (или 25) | В pom другая версия, чем установленный JDK | Выровнять source/target/release с Project SDK |
| Окно "мигает" и закрывается | Нет launch() или show() | Application.launch(...) и stage.show() |
Module javafx.controls not found | OpenJFX не подтянулся / неверный module path | Maven Reload, javafx:run или VM options |
IllegalStateException: Not on FX application thread | UI меняют из фонового потока | Platform.runLater(() -> …) |
| Интерфейс "зависает" | Долгая работа в setOnAction | Task / фоновый поток + runLater |
JavaFX runtime components are missing | Запуск только через classpath без --module-path | VM options или mvn javafx:run |
javafx:run не стартует | Неверный mainClass в плагине | Указать класс с public static void main |
| Код компилируется, Run в IDEA падает | Временная конфигурация без VM options | Сохранить конфигурацию с --module-path после compile |
Подробнее про экосистему и контейнеры — JavaFX и GUI.
Что попробовать
- Соберите DemoApp и переключите
MainнаApplication.launch(DemoApp.class, args). - Перепишите форму конвертера на
VBox— метка, поле, результат, ряд кнопок. - Добавьте CSS:
scene.getStylesheets().add(".../app.css")и класс-fx-font-size. - Сохраните результат в файл через
FileChooser.showSaveDialog. - Вынесите разметку в FXML — см. JavaFX и GUI.
Дальше
| Цель | Материал |
|---|---|
| Справочник API | Справочник по JavaFX и Swing — элементы UI |
| Swing в JDK без Maven | Простые приложения на Java — Swing |
| Потоки и UI | JVM, память и потоки |
| Теория десктопа | Архитектура десктопных приложений · Особенности разработки десктопных приложений · раздел "Десктопные приложения" |
В подборках
Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:
Первые шаги (маршрут подборки) — Первая программа на Spring Framework, EF Core — первая программа, Первая программа на JavaBean, ADO.NET / Dapper — первая программа, Первая программа на JavaServer Faces, Первая программа на ASP.NET Core.