→ Slide 1

JavaFX i OpenJFX

→ Slide 2
  • JavaFX - zestaw bibliotek do tworzenia nowoczesnych aplikacji desktopowych w Javie
  • Od JDK 8 wbudowany w Oracle JDK jako nowoczesna alternatywa dla AWT/Swing
  • Od JDK 11 nie jest już częścią JDK, rozwijany osobno jako OpenJFX
  • OpenJFX - otwartoźródłowy projekt rozwijający JavaFX przez społeczność, Oracle i firmę Gluon
  • Aktualne wydania: Gluon OpenJFX
→ Slide 3
  • architektura oparta o graf Scene Graph
  • deklaratywne UI (FXML)
  • stylowanie CSS
  • multimedia, wsparcie dla animacji
  • obsługa grafiki 2D i 3D oraz renderowania sprzętowego (akceleracja GPU)
  • duża liczba wbudowanych kontrolek i możliwość rozbudowy
  • WebView, silnik WebKit do osadzania treści webowej
  • integracja z Swing (SwingNode)
  • mechanizm danych reaktywnych (Observable, Property, Binding)
→ Slide 4

→ Slide 5
  • Prism - akcelerowany potok renderowania (GPU: DirectX/OpenGL).
    W przypadku braku wsparcia sprzętowego JavaFX przechodzi w tryb renderowania programowego.
  • Glass Windowing Toolkit - warstwa okien, zdarzeń i timerów; interfejs między JavaFX a systemem operacyjnym.
  • WebView - kontrolka osadzająca HTML/JS (silnik WebKit) umożliwiająca renderowanie i interakcję z treścią webową.
  • Media Engine - silnik multimediów (audio/video) obsługujący różne formaty i kodeki.
  • Quantum Toolkit - łączy warstwy niskopoziomowe z API JavaFX.
  • Scene Graph - drzewo węzłów UI i grafiki
  • JavaFX API - publiczne klasy i interfejsy do budowy interfejsu graficznego
→ Slide 6
  • javafx.base - podstawowe klasy, kolekcje, properties
  • javafx.graphics - grafika 2D/3D, scena, renderowanie
  • javafx.controls - gotowe kontrolki UI
  • javafx.fxml - FXML, loader, kontrolery
  • javafx.media - multimedia
  • javafx.web - WebView, silnik WebKit
  • javafx.swing - integracja z Swing (SwingNode)
→ Slide 7

141_1.jpg

  • Stage - okno aplikacji - scena teatralna, gdzie odbywa się przedstawienie
  • Scene - zawartość okna - konkretna scenografia, rekwizyty i aktorzy ustawieni na scenie, zawiera hierarchię węzłów (Scene Graph)
  • Node - elementy UI, grafiki
→ Slide 8

137_1.jpg

  • Każdy element UI (obiekty 2D/3D, kontrolki UI, kontenery, elementy multimedialne) jest węzłem grafu (Node)
  • Renderowanie, transformacje i zdarzenia propagują się po drzewie
  • Zmiana stanu węzła natychmiast widoczna w interfejsie
→ Slide 9
  • Scene to główny kontener, w którym umieszczamy hierarchię węzłów
  • Korzeń (Root) - główny węzeł sceny, zwykle layout (np. VBox, BorderPane)
  • Węzły rodzicielskie (Parent) - zawierają inne węzły (dzieci)
    • elementy grupujące (Group),
    • układy (Pane, HBox, StackPane, GridPane)
    • kontrolki (Button, TextField, Label), też są rodzicami i mogą zawierać wewnętrzne węzły
  • Liście - elementy graficzne 2D/3D (Shape, ImageView, Text, SwingNode)
VBox root = new VBox();
Label title = new Label("Kurs JavaFX");
Button button = new Button("Kliknij");
root.getChildren().addAll(title, button);
 
Scene scene = new Scene(root, 600, 400);
→ Slide 10
  • Punkt wejścia: klasa dziedzicząca po Application
  • Kluczowa metoda: start(Stage stage) udostępniająca główny Stage.
    W metodzie start():
    • przygotowujemy scenę (tworzymy węzły, układamy je w layoutach)
    • ustawiamy Stage (tytuł, rozmiar, scenę)
    • wywołujemy show(), aby wyświetlić okno.
  • Uruchomienie: launch(args) w main()
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
 
public class HelloFxApp extends Application {
    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Label("Witaj JavaFX"), 400, 200);
        stage.setTitle("Hello FX");
        stage.setScene(scene);
        stage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}
→ Slide 11

JavaFX nie jest już częścią JDK. Istnieje kilka sposobów na dodanie JavaFX do projektu:

  • instalacja ręczna - pobierz OpenJFX, dodaj biblioteki do projektu, ustaw VM options
  • z użyciem Maven lub Gradle - dodaj zależności do pom.xml lub build.gradle
  • szablon projektu JavaFX w IntelliJ (plugin JavaFX, używa Maven) - brakujące zależności powinny zostać pobrane automatycznie

Dokumentacja OpenJFX zawiera szczegółowe instrukcje dla różnych IDE i systemów budowania.

→ Slide 12
  • Pobierz SDK JavaFX z Gluon lub Oracle zgodny z wersją JDK
  • Dodaj biblioteki JavaFX do projektu
    FileProject StructureLibraries
  • Ustaw ścieżkę do modułu w VM options przy uruchamianiu:
    BuildEdit ConfigurationsVM options
    --module-path "ścieżka/do/javafx-sdk-XX/lib" --add-modules javafx.controls,javafx.fxml
→ Slide 13

Maven - system zarządzania projektem i budowania, który automatycznie pobiera zależności i konfiguruje projekt. Jak skonfigurować JavaFX z Maven:

  1. Utwórz projekt Maven (lub użyj archetypu JavaFX)
  2. Wyszukaj JavaFX w repozytorium Maven Maven Central lub https://mvnrepository.com/
  3. Dodaj zależności javafx-controls (opcjonalnie javafx-fxml) do pliku pom.xml
  4. Skonfiguruj plugin javafx-maven-plugin do uruchamiania (zdarzenie javafx:run) oraz maven-compiler-plugin
  5. Uruchamiaj (z CLI) przez zadanie Maven, z okna narzędzi Maven IDE albo skonfiguruj IDE
    mvn javafx:run

JavaFX Maven Project in IntelliJ IDEA from Scratch

→ Slide 14
<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>26</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>26</version>
        <scope>compile</scope>
    </dependency>
</dependencies>
→ Slide 15
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.15.0</version>
            <configuration>
                <source>25</source>  <!-- Match your JDK version -->
                <target>25</target>
            </configuration>
        </plugin>
 
        <plugin>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-maven-plugin</artifactId>
            <version>0.0.8</version>
            <executions>
                <execution>
                    <id>default-cli</id>
                    <configuration>
                        <mainClass>MyJavaFXApp</mainClass>  <!-- Your main class -->
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
→ Slide 16
  • Opcja 1: Okno narzędzi Maven
    • Lifecycleclean
    • Pluginsjavafxjavafx:run
  • Opcja 2: Dodaj konfigurację uruchomienia w IntelliJ
    • RunEdit Configurations
    • Dodaj +Maven
    • W polu Command line wpisz: clean javafx:run
→ Slide 17
  • JavaFX ma własny wątek UI (JavaFX Application Thread) uruchamiany automatycznie przy starcie aplikacji
  • Wszystkie operacje na węzłach muszą być wykonywane w tym wątku
  • Aby zachować responsywność, długotrwałe zadania powinny być wykonywane w osobnych wątkach, np. przez Task lub Service, a aktualizacje UI przez Platform.runLater()
→ Slide 18
→ Slide 19
  • javafx.scene.layout - układy (layout) - specjalne kontenery, które automatycznie rozmieszczają swoje dzieci według określonych reguł
  • HBox, VBox, ButtonBar - układ poziomy/pionowy, rozmieszcza dzieci w linii
  • BorderPane - układ z pięcioma regionami (top, bottom, left, right, center)
  • SplitPane - układ dzielący przestrzeń na panele o regulowanej wielkości
  • GridPane - układ siatkowy, rozmieszcza dzieci w wierszach i kolumnach
  • FlowPane - układ przepływowy, rozmieszcza dzieci w linii, zawija do następnego wiersza
  • AnchorPane - układ kotwiczący, pozwala przypiąć dzieci do krawędzi
  • StackPane - nakłada dzieci jedno na drugie, centrowanie
StackPane stackPane = new StackPane();
 
Ellipse ellipse = new Ellipse(110, 70);
ellipse.setFill(Color.LIGHTBLUE);
 
Text text = new Text("Hello World");
text.setFont(new Font("Arial Bold", 24));
 
stackPane.getChildren().addAll(ellipse, text);
Scene scene = new Scene(stackPane, 350, 230, Color.LIGHTYELLOW);
→ Slide 20
  • Effect - wbudowane efekty graficzne do stylizacji węzłów:
    • DropShadow - efekt cienia, można dostosować kolor, rozmycie i przesunięcie
    • Reflection - efekt odbicia, można dostosować wysokość i rozmycie
  • Wypełnienie kształtu:
    • Color, ImagePattern, RadialGradient
    • LinearGradient - gradient liniowy, definiowany przez punkty początkowy i końcowy oraz zestaw Stop (pozycja + kolor)
Stop[] stops = new Stop[] { 
        new Stop(0, Color.DODGERBLUE),
        new Stop(0.5, Color.LIGHTBLUE),
        new Stop(1.0, Color.LIGHTGREEN)  };
LinearGradient gradient = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE, stops);
ellipse.setFill(gradient);
 
ellipse.setEffect(new DropShadow(30, 10, 10, Color.GRAY));
→ Slide 21
RotateTransition rotate = new RotateTransition(Duration.millis(2500), stackPane);
rotate.setToAngle(360);
rotate.setFromAngle(0);
rotate.setInterpolator(Interpolator.LINEAR);
rotate.play();
→ Slide 22
  • Event - obsługa zdarzeń (myszy, klawiatury, akcji)
  • Można rejestrować nasłuchiwacze zdarzeń na węzłach grafu
  • Zdarzenia propagują się po drzewie, rodzic może przechwycić zdarzenie dziecka
  • setOnAction(EventHandler<ActionEvent>), setOnMouseClicked(EventHandler<MouseEvent>) dla kontrolek (węzłów) obsługujących konkretne zdarzenia,
  • addEventHandler(EventType<T>, EventHandler<T>) dla ogólnych zdarzeń
stackPane.setOnMouseClicked(mouseEvent -> {
    if (rotate.getStatus().equals(Animation.Status.RUNNING)) {
        rotate.pause();
    } else {
        rotate.play();
    }
});
→ Slide 23
  • JavaFX posiada wbudowany mechanizm danych reaktywnych, możemy wiązać właściwości węzłów z innymi właściwościami lub źródłami danych, tak aby zmiany były automatycznie odzwierciedlane w interfejsie
  • właściwości węzłów są obserwowalne np. textProperty() dla Label czy TextField, możemy ustawić nasłuchiwacz reagujący na zmiany lub powiązać je z innymi właściwościami
  • Observable oraz ObservableValue - interfejsy reprezentujące obserwowalną wartość, posiadają metody do dodawania nasłuchiwaczy addListener() i pobierania aktualnej wartości
→ Slide 24
rotate.statusProperty().addListener(new InvalidationListener() {
    @Override
    public  void invalidated(Observable observable) {
        text.setText("Animation status: " +
        ((ObservableObjectValue<Animation.Status>)observable).getValue());
    }
});

lub krócej

rotate.statusProperty().addListener(observable -> {
    text.setText("Animation status: " + rotate.getStatus());
});
→ Slide 25
  • Binding pozwala na tworzenie dynamicznych powiązań między właściwościami implementującymi interfejs Property<T> bez potrzeby dodawania ręcznych nasłuchiwaczy
  • bind() - tworzy jednokierunkowe powiązanie, gdzie jedna właściwość jest zależna od drugiej
text.rotateProperty().bind(stackPane.rotateProperty());
  • przekształcanie wartości do tworzenia złożonych powiązań, np. matematycznych operacji, warunków logicznych, formatowania tekstu
text2.textProperty().bind(stackPane.rotateProperty().asString("%.1f"));
  • łączenie wielu właściwości i operacji na nich, np. konkatenacja tekstu z dwóch TextField do Label
TextField firstName = new TextField();
TextField lastName = new TextField();
Label fullName = new Label();
 
fullName.textProperty().bind(
    firstName.textProperty().concat(" ").concat(lastName.textProperty())
);
→ Slide 26
  • bindBidirectional() - dwukierunkowe powiązanie, zmiana jednej właściwości automatycznie aktualizuje drugą i odwrotnie
  • przydatne w formularzach, gdzie chcemy, aby zmiany w UI były odzwierciedlane w modelu danych i na odwrót
firstName.textProperty().bindBidirectional(model.firstNameProperty());
→ Slide 27
  • FXML to XML opisujący hierarchię UI, pozwala na oddzielenie logiki od warstwy prezentacji
  • FXMLLoader wczytuje i parsuje plik FXML, tworzy obiekty UI i ustawia je w hierarchii
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>  
 
<VBox xmlns:fx="http://javafx.com/fxml">
    <Button text="OK" />
    <Label text="Witaj JavaFX" />
</VBox>
FXMLLoader loader = new FXMLLoader(getClass().getResource("main-view.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
→ Slide 28

Scene Builder in Action

→ Slide 29
  • Instancje klas, np. <Button> tworzy obiekt klasy javafx.scene.control.Button
  • Własności obiektów (settery, kolekcje)
    <Button>
        <text>Hello, World!</text>
    </Button>
  • Własności statyczne, np. GridPane.rowIndex
    <Label text="My Label">
      <GridPane.rowIndex>0</GridPane.rowIndex>
      <GridPane.columnIndex>0</GridPane.columnIndex>
    </Label>
  • Element <children> jest używany do definiowania listy dzieci
    <VBox>
        <children>
            <Button text="OK" />
            <Label text="Witaj JavaFX" />
        </children>
    </VBox>
  • Klasy mogą definiować domyślne własności, np. <children> dla węzłow rodzicielskich (np. VBox i HBox)
    <VBox>
        <Button text="OK" />
        <Label text="Witaj JavaFX" />
    </VBox>
→ Slide 30
  • Atrybuty elementów mapują się na właściwości obiektów, np. text=„OK” dla Button ustawia właściwość text obiektu Button
      <Button text="OK" />
     
  • Można używać atrybutów do ustawiania prostych wartości, takich jak tekst, kolory, rozmiary, ale bardziej złożone struktury (np. hierarchia węzłów) wymagają elementów zagnieżdżonych
  • Ustawianie własności statycznych
      <Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
     
  • Specjalne atrybuty FXML:
    • fx:controller - określa klasę kontrolera powiązaną z tym widokiem
    • fx:id - identyfikator węzła, pozwala m.in. na powiązanie z polem kontrolera
    • onAction - przypisuje metodę obsługi zdarzenia (np. kliknięcia) z kontrolera
  • Powiązanie z własnością kontrolera
    <Label text="${controller.someProperty}" />


    Atrybut text jest ustawiany na wartość właściwości someProperty z kontrolera, a aktualizacje tej właściwości będą automatycznie odzwierciedlane w interfejsie.

→ Slide 31
  • Wzorzec MVC: pliki FXML definiują strukturę interfejsu (widok), a kontrolery Java obsługują logikę i zdarzenia
// MainController.java
public class MainController {
    @FXML 
    private TextField input;
    @FXML 
    private Label output;
 
    @FXML
    private void onGenerate() {
        output.setText("Witaj, " + input.getText().trim());
    }
}
<!-- main-view.fxml -->
<VBox spacing="8" xmlns:fx="http://javafx.com/fxml" fx:controller="demo.MainController">
    <TextField fx:id="input" promptText="Podaj imię" />
    <Button text="Generuj" onAction="#onGenerate" />
    <Label fx:id="output" />
</VBox>
→ Slide 32
  • Metoda initialize() interfejsu Initializable w kontrolerze jest wywoływana automatycznie po załadowaniu FXML i wstrzyknięciu zależności
  • Można jej użyć do dodatkowej konfiguracji elementów UI, ustawienia nasłuchiwaczy, inicjalizacji danych
public class MainController implements Initializable {
    @FXML private TextField input;
    @FXML private Label output;
 
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        input.setText("Jan");
        output.setText("Witaj, " + input.getText());
    }
}
→ Slide 33
  • FXMLLoader - klasa odpowiedzialna za wczytywanie i parsowanie plików FXML, tworzenie obiektów UI i hierarchii
    • load() - wczytuje FXML i zwraca korzeń hierarchii (Parent)
    • getController() - zwraca instancję kontrolera powiązanego z tym FXML
    • setLocation(URL) - ustawia lokalizację FXML
    • setResources(ResourceBundle) - ustawia zasoby (dla internacjonalizacji)
URL location = getClass().getResource("example.fxml");
ResourceBundle resources = ResourceBundle.getBundle("com.foo.example");
FXMLLoader fxmlLoader = new FXMLLoader(location, resources);
 
Pane root = (Pane)fxmlLoader.load();
MyController controller = (MyController)fxmlLoader.getController();
→ Slide 34
  1. Odczyt pliku FXML, tworzenie obiektów i ustawienie właściwości węzłów
  2. Utworzenie instancji kontrolera (jeśli fx:controller jest określony)
  3. Wstrzyknięcie zależności do kontrolera (pola publiczne lub oznaczone @FXML i odpowiadające im fx:id w FXML)
  4. Rejestrowanie nasłuchiwaczy zdarzeń (np. onAction=#methodName)
  5. Wywołanie metody initialize() kontrolera (jeśli implementuje Initializable lub posiada metodę oznaczoną @FXML o nazwie initialize)
→ Slide 35
  • Stylowanie podobne do webowego CSS, ale z prefiksem -fx-
  • Plik CSS podpinamy pod scenę lub dowolnego węzła rodzicielskiego:
      scene.getStylesheets().add(getClass().getResource("Style.css").toExternalForm()); 
  • Style można dodać w FXML
    <stylesheets>
       <URL value="@demo.css" />
    </stylesheets>
     
    <VBox stylesheet="@Style.css">
       <Button text="Submit" id="primaryButton" styleClass="primaryButton"/>
    </VBox>
  • Style propagują się w dół hierarchii
  • domyslny plik CSS dla JavaFX: modena.css - zawiera style dla wszystkich wbudowanych kontrolek, można go nadpisać lub rozszerzyć
→ Slide 36
  • Bezpośredni styl dla pojedynczego węzła (style inline)
    button.setStyle("-fx-font-size: 16px; ");
  • Selektory CSS w pliku CSS (klasy, identyfikatory) pozwalają na grupowanie i ponowne użycie stylów
  • Style można określić w FXML (właściwości styleClass i id) i konfigurować w Scene Builder
    <Label text="Hello" styleClass="greeting"/>
    <Button text="Submit" id="primaryButton"/>
  • Style można określić w kodzie Java, dodając klasy CSS do węzłów lub ustawiając identyfikatory
label.getStyleClass().add("greeting");
button.setId("primaryButton");
→ Slide 37
/* Plik CSS dla JavaFX */
/* Selekcja po nazwie węzła */
Button {
    -fx-background-color: lightblue;
    -fx-text-fill: darkblue;
}
 
/* Selekcja po klasie CSS */
.primaryButton {
    -fx-background-color: linear-gradient(to right, #ff7e5f, #feb47b);
    -fx-text-fill: white;  
    -fx-font-size: 16px;
    -fx-padding: 10px 20px;
    -fx-background-radius: 8px;
}
 
/* Selekcja po identyfikatorze */
#primaryButton {
    -fx-background-color: linear-gradient(to right, #ff7e5f, #feb47b);
    -fx-text-fill: white;   
    -fx-font-size: 16px;
    -fx-padding: 10px 20px;
    -fx-background-radius: 8px;
}
 
/* Selekcja pseudoklasy */
Button:hover {
    -fx-background-color: linear-gradient(to right, #feb47b, #ff7e5f);
}
→ Slide 38
  • Styl inline
  • Identyfikator CSS (#id)
  • Klasa CSS (.class)
  • Styl domyślny węzła
→ Slide 39
→ Slide 40

Stwórz prostą aplikację JavaFX do zarządzania listą zakupów.

Wymagania:

  • Aplikacja powinna umożliwiać dodawanie nowych pozycji do listy oraz usuwanie zaznaczonych pozycji.
  • Użyj ListView do wyświetlania listy, TextField do wprowadzania nowych pozycji
  • Przycisk Dodaj powinien być aktywny tylko wtedy, gdy pole tekstowe nie jest puste (użyj wiązania)
  • Przycisk Usuń powinien usuwać aktualnie zaznaczoną pozycję z listy (lub wiele zaznaczonych pozycji)
  • Zadeklaruj widok w FXML. Wygląd aplikacji dowolny. Zaprojektuj samodzielnie, najlepiej z pomocą Scene Buildera
  • Zdefiniuj klasę kontrolera i powiąż go z widokiem
  • Zastosuj stylowanie CSS do poprawy wyglądu interfejsu

Wskazówki:

  • disableProperty().bind(…) dla przycisku
  • getSelectionModel().getSelectedIndex() dla usuwania
  • Puste wejście ignoruj przez trim().isEmpty()