Scene GraphSwingNode)Observable, Property, Binding)javafx.base - podstawowe klasy, kolekcje, properties javafx.graphics - grafika 2D/3D, scena, renderowaniejavafx.controls - gotowe kontrolki UIjavafx.fxml - FXML, loader, kontroleryjavafx.media - multimediajavafx.web - WebView, silnik WebKit javafx.swing - integracja z Swing (SwingNode)Scene Graph)Node)Scene to główny kontener, w którym umieszczamy hierarchię węzłówRoot) - główny węzeł sceny, zwykle layout (np. VBox, BorderPane)Parent) - zawierają inne węzły (dzieci)Group), Pane, HBox, StackPane, GridPane)Button, TextField, Label), też są rodzicami i mogą zawierać wewnętrzne węzłyShape, 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);
Applicationstart(Stage stage) udostępniająca główny Stage. start():Stage (tytuł, rozmiar, scenę)show(), aby wyświetlić okno.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); } }
JavaFX nie jest już częścią JDK. Istnieje kilka sposobów na dodanie JavaFX do projektu:
pom.xml lub build.gradleDokumentacja OpenJFX zawiera szczegółowe instrukcje dla różnych IDE i systemów budowania.
File → Project Structure → LibrariesBuild → Edit Configurations → VM options --module-path "ścieżka/do/javafx-sdk-XX/lib" --add-modules javafx.controls,javafx.fxml
Maven - system zarządzania projektem i budowania, który automatycznie pobiera zależności i konfiguruje projekt. Jak skonfigurować JavaFX z Maven:
javafx-controls (opcjonalnie javafx-fxml) do pliku pom.xml javafx-maven-plugin do uruchamiania (zdarzenie javafx:run) oraz maven-compiler-pluginmvn javafx:run
<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>
<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>
Lifecycle → clean Plugins → javafx → javafx:runRun → Edit Configurations+ → MavenCommand line wpisz: clean javafx:runJavaFX Application Thread) uruchamiany automatycznie przy starcie aplikacji Task lub Service, a aktualizacje UI przez Platform.runLater()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 liniiBorderPane - układ z pięcioma regionami (top, bottom, left, right, center)SplitPane - układ dzielący przestrzeń na panele o regulowanej wielkościGridPane - układ siatkowy, rozmieszcza dzieci w wierszach i kolumnachFlowPane - układ przepływowy, rozmieszcza dzieci w linii, zawija do następnego wierszaAnchorPane - układ kotwiczący, pozwala przypiąć dzieci do krawędziStackPane - nakłada dzieci jedno na drugie, centrowanieStackPane 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);
Color, ImagePattern, RadialGradientStop (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));
Transition i TimelineFadeTransition, RotateTransition, ScaleTransition, TranslateTransition - gotowe klasy do animacji określonych właściwościTimeline - bardziej elastyczna klasa do animowania dowolnych właściwości w określonych punktach czasowychParallelTransition i SequentialTransition - do łączenia wielu animacjiplay() - uruchamia animację, stop() - zatrzymuje, pause() - wstrzymuje, getStatus() - sprawdza status animacjiRotateTransition rotate = new RotateTransition(Duration.millis(2500), stackPane); rotate.setToAngle(360); rotate.setFromAngle(0); rotate.setInterpolator(Interpolator.LINEAR); rotate.play();
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(); } });
textProperty() dla Label czy TextField, możemy ustawić nasłuchiwacz reagujący na zmiany lub powiązać je z innymi właściwościamiaddListener() i pobierania aktualnej wartościrotate.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()); });
bind() - tworzy jednokierunkowe powiązanie, gdzie jedna właściwość jest zależna od drugiejtext.rotateProperty().bind(stackPane.rotateProperty());
text2.textProperty().bind(stackPane.rotateProperty().asString("%.1f"));
TextField do LabelTextField firstName = new TextField(); TextField lastName = new TextField(); Label fullName = new Label(); fullName.textProperty().bind( firstName.textProperty().concat(" ").concat(lastName.textProperty()) );
bindBidirectional() - dwukierunkowe powiązanie, zmiana jednej właściwości automatycznie aktualizuje drugą i odwrotniefirstName.textProperty().bindBidirectional(model.firstNameProperty());
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);
<Button> tworzy obiekt klasy javafx.scene.control.Button<Button> <text>Hello, World!</text> </Button>
GridPane.rowIndex <Label text="My Label"> <GridPane.rowIndex>0</GridPane.rowIndex> <GridPane.columnIndex>0</GridPane.columnIndex> </Label>
<children> jest używany do definiowania listy dzieci <VBox> <children> <Button text="OK" /> <Label text="Witaj JavaFX" /> </children> </VBox>
<children> dla węzłow rodzicielskich (np. VBox i HBox) <VBox> <Button text="OK" /> <Label text="Witaj JavaFX" /> </VBox>
text=„OK” dla Button ustawia właściwość text obiektu Button <Button text="OK" />
<Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
fx:controller - określa klasę kontrolera powiązaną z tym widokiemfx: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<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.
// 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>
initialize() interfejsu Initializable w kontrolerze jest wywoływana automatycznie po załadowaniu FXML i wstrzyknięciu zależnościpublic 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()); } }
FXMLLoader - klasa odpowiedzialna za wczytywanie i parsowanie plików FXML, tworzenie obiektów UI i hierarchiiload() - wczytuje FXML i zwraca korzeń hierarchii (Parent)getController() - zwraca instancję kontrolera powiązanego z tym FXMLsetLocation(URL) - ustawia lokalizację FXMLsetResources(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();
fx:controller jest określony)@FXML i odpowiadające im fx:id w FXML)onAction=#methodName)initialize() kontrolera (jeśli implementuje Initializable lub posiada metodę oznaczoną @FXML o nazwie initialize) -fx-scene.getStylesheets().add(getClass().getResource("Style.css").toExternalForm());
<stylesheets> <URL value="@demo.css" /> </stylesheets> <VBox stylesheet="@Style.css"> <Button text="Submit" id="primaryButton" styleClass="primaryButton"/> </VBox>
modena.css - zawiera style dla wszystkich wbudowanych kontrolek, można go nadpisać lub rozszerzyćbutton.setStyle("-fx-font-size: 16px; ");
styleClass i id) i konfigurować w Scene Builder <Label text="Hello" styleClass="greeting"/> <Button text="Submit" id="primaryButton"/>
label.getStyleClass().add("greeting"); button.setId("primaryButton");
/* 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); }
#id).class)Stwórz prostą aplikację JavaFX do zarządzania listą zakupów.
Wymagania:
ListView do wyświetlania listy, TextField do wprowadzania nowych pozycjiDodaj powinien być aktywny tylko wtedy, gdy pole tekstowe nie jest puste (użyj wiązania)Usuń powinien usuwać aktualnie zaznaczoną pozycję z listy (lub wiele zaznaczonych pozycji)Wskazówki:
disableProperty().bind(…) dla przyciskugetSelectionModel().getSelectedIndex() dla usuwaniatrim().isEmpty()