JavaFX#

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

  • Современный API для создания UI

  • Поддержку аппаратного ускорения графики

  • Встроенную поддержку CSS для стилизации

  • FXML для декларативного описания интерфейсов

  • Поддержку MVVM паттерна

Основные пакеты для разработки с JavaFX с использованием OpenJDK#

Пакет

Версия

Репозиторий

Описание

openjdk-17-jdk

17.0.13+11-2~deb12u1

extended

Комплект разработки ПО на Java

openjfx

11.0.11+1-4

extended

Платформа JavaFX/OpenJFX для графических приложений для Java

maven

3.8.7-1

extended

Инструмент для автоматической сборки проектов на Java и др. языках

gradle

4.4.1-18+b1

extended

Система для автоматической сборки проектов (в т.ч. на Java)

Дополнительные пакеты (кроме IDE)#

Пакет

Версия

Репозиторий

Описание

Scene Builder (от Gluon)

23.0.1

gluon

Визуальные конструктор FXML файлов

JavaFX Scene Builder от Oracle

2.0

oracle

Инструмент визуальной разработки FXML файлов

ControlsFX

11.2.2

github

Дополнительные компоненты UI

JFoenix

9.0.10

github

Material Design компоненты

Разработка пользовательского интерфейса с помощью Scene Builder (от Gluon)#

Пункт 1#

  • Установка OpenJDK:

sudo apt install openjdk-17-jdk

Пункт 2#

  • Для установки Scene Builder необходимо загрузить пакет по ссылке.

Примечание

Пакет libffi7 находится в зависимостях Scene Builder, он отдельно устанавливается по ссылке.

Пункт 3#

  • После установки Scene Builder необходимо запустить из меню пуск. Создать интерфейс можно перетаскиванием виджетов из панели. Файл сохраняется с расширением FXML.

Дополнительные компоненты#

Список виджетов можно расширить с помощью пакета ControlsFX. ControlsFX — это библиотека-надстройка над JavaFX, которая добавляет новые компоненты и улучшает стандартные.

Maven#

  • Для этого в проекте Maven необходимо в файле pom.xml указать следующие библиотеки:

<!-- JavaFX -->
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>17</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-fxml</artifactId>
    <version>17</version>
</dependency>

<!-- ControlsFX -->
<dependency>
    <groupId>org.controlsfx</groupId>
    <artifactId>controlsfx</artifactId>
    <version>11.1.2</version>
</dependency>
  • После требуется обновить зависимости проекта:

mvn dependency:resolve

Примечание

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

Gradle#

  • Если проект под управлением Gradle, необходимо в файле build.gradle задать следующие библиотеки:

dependencies {
    // JavaFX
    implementation 'org.openjfx:javafx-controls:17'
    implementation 'org.openjfx:javafx-fxml:17'

    // ControlsFX
    implementation 'org.controlsfx:controlsfx:11.1.2'
}
  • После требуется обновить зависимости проекта:

gradle dependencies --refresh-dependencies

Создание JavaFX приложения с Maven#

Ниже представлен пример создания приложения, используя стандартные виджеты JavaFX. В пункте Создание JavaFX приложения с Gradle описано создание аналогичного приложения, с использованием контроллера и макета приложения с помощью FXML файла.

Пункт 1#

  • Установка OpenJDK:

sudo apt install openjdk-17-jdk
sudo apt install maven

Пункт 2#

  • Создание проекта и ручной переход в директорию javafx-userinfo-app:

mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=javafx-userinfo-app \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false

Пункт 3#

  • Добавление зависимостей перезаписью pom.xml файла:

<properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <maven.compiler.source>17</maven.compiler.source>
     <maven.compiler.target>17</maven.compiler.target>
     <javafx.version>17.0.2</javafx.version>
</properties>

<dependencies>
     <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-controls</artifactId>
         <version>${javafx.version}</version>
     </dependency>
     <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-graphics</artifactId>
         <version>${javafx.version}</version>
     </dependency>
</dependencies>

<build>
     <plugins>
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-compiler-plugin</artifactId>
             <version>3.8.1</version>
         </plugin>
         <plugin>
             <groupId>org.openjfx</groupId>
             <artifactId>javafx-maven-plugin</artifactId>
             <version>0.0.8</version>
             <configuration>
                 <mainClass>com.example.App</mainClass>
             </configuration>
         </plugin>
     </plugins>
</build>

Пункт 4#

  • Создание основного класса в src/main/java/com/example/App.java файле:

package com.example;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class App extends Application {

    @Override
    public void start(Stage primaryStage) {
        Label label = new Label();
        Button button = new Button("Узнать пользователя");

        button.setOnAction(e -> {
            if (button.getText().equals("Узнать пользователя")) {
                String user = System.getProperty("user.name");
                label.setText("Текущий пользователь: " + user);
                button.setText("Очистить");
            } else {
                label.setText("");
                button.setText("Узнать пользователя");
            }
        });

        VBox root = new VBox(10, button, label);
        Scene scene = new Scene(root, 300, 200);

        primaryStage.setTitle("JavaFX App");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Пункт 5#

  • Сборка и запуск приложения:

mvn clean javafx:run

Сборка и упаковка#

Для упаковки JavaFX приложения рекомендуется создавать самодостаточный исполняемый JAR (fat-jar). На его основе создается deb-пакет с помощью утилиты из состава JDK jpackage.

Пункт 1#

  • Добавление плагина создания fat-jar:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.3.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.App</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Пункт 2#

  • Сборка fat-jar (предварительно рекомендуется удалить файлы тестов из директории src/test/java/com/example):

mvn clean package

Пункт 3#

  • Создание deb-пакета (используя jpackage)

jpackage --input target/ \
--name javafx-userinfo-app \
--main-jar javafx-userinfo-app-1.0.jar \
--main-class com.example.App \
--type deb \
--java-options '--enable-preview' \
--runtime-image $JAVA_HOME

Создание JavaFX приложения с Gradle#

Ниже представлен пример создания приложения с использованием контроллера и макета приложения с помощью FXML файла. В пункте Создание JavaFX приложения с Maven описано создание аналогичного приложения без использования MVVM паттерна. Представлен вариант создания deb-пакета используя встроенные средства Gradle (требуемая версия <= 8.8), иначе можно воспользоваться jpackage, как описано выше.

Пункт 1#

  • Установка OpenJDK и Gradle 8.14 с помощью SDKMAN <https://sdkman.io/install/>__ (инструмент управления версиями пакетов):

sudo apt install openjdk-17-jdk

# Установка SDKMAN
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

# Установка последнюю версию Gradle
sdk install gradle

# Проверка версии Gradle
gradle -v

Примечание

Для работы с Java 17+ рекомендуется Gradle 7.0+.

Пункт 2#

  • После создания каталога проекта необходимо выполнить генерацию шаблонного проекта в новой директории:

gradle init --type java-application
  • Обновление gradle до версии 8.8:

./gradlew wrapper --gradle-version 8.8

Пункт 3#

  • Добавление JavaFX в зависимости перезаписью build.gradle файла и добавление инструкции создания deb-пакета:

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.13' // Плагин JavaFX

    // Плагины создания deb-пакета
    id 'org.beryx.jlink' version '2.26.0'
    id 'org.javamodularity.moduleplugin' version '1.8.12'
}

group 'com.example'
version '1.0'

repositories {
    mavenCentral()
}

// Задание основных класса и модуля проекта (для deb-пакета)
application {
    mainModule = 'com.example'
    mainClass = 'com.example.MainApp'
}

javafx {
    version = "17.0.2"
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}

dependencies {
    implementation 'org.openjfx:javafx-controls:17'
    implementation 'org.openjfx:javafx-fxml:17'
}

task createFatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'com.example.MainApp'
    }
    archiveBaseName = 'user-info-app'
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    with jar
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

// Инструкция сборки модулей и создания deb-пакета
jlink {
    launcher {
        name = 'App'
    }

    // Инструкция создания deb пакета
    jpackage {
        installerType = 'deb'
        installerOptions = [
            '--linux-menu-group', 'Development',
            '--linux-shortcut'
        ]
    }
}

Пункт 4#

  • Настройка module-info.java файла:

module com.example {
    requires javafx.controls;
    requires javafx.fxml;


    opens com.example to javafx.fxml;
    exports com.example;
}

Пункт 5#

  • Создание основного класса src/main/java/com/example/MainApp.java:

package com.example;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MainApp extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/example/main.fxml"));
        Parent root = loader.load();

        primaryStage.setTitle("User Info App");
        primaryStage.setScene(new Scene(root, 300, 200));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Пункт 6#

  • Создание класса контроллера в src/main/java/com/example/UserInfoController.java файле:

package com.example;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;

public class UserInfoController {
    @FXML private Label infoLabel;
    @FXML private Button actionButton;

    @FXML
    private void handleButtonAction() {
        if (actionButton.getText().equals("Узнать пользователя")) {
            String user = System.getProperty("user.name");
            infoLabel.setText("Текущий пользователь: " + user);
            actionButton.setText("Очистить");
        } else {
            infoLabel.setText("");
            actionButton.setText("Узнать пользователя");
        }
    }
}

Пункт 7#

  • Создание интерфейса с помощью файла FXML src/main/resources/com/example/main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="com.example.UserInfoController" spacing="10" alignment="CENTER">
    <Button fx:id="actionButton" text="Узнать пользователя" onAction="#handleButtonAction"/>
    <Label fx:id="infoLabel" />
</VBox>

Пункт 8#

  • Запуск приложения:

gradle clean run

Сборка и упаковка#

Создание deb-пакет с помощью сценария jpackage, прописанного в файле build.gradle:

./gradlew jpackage