W poprzednich lekcjach poświęconych JavieFX pokazałem jak tworzyć proste aplikacje przy użyciu czystego kodu Javy. Możliwe jest jednak inne podejście, w którym interfejs graficzny jest zdefiniowany w osobnym pliku FXML. Co prawda, można taki plik edytować ręcznie, ale można sobie też pomóc Scene Builderem, który pozwala na budowanie interfejsu w sposób „wizualny”.
Aplikacja JavaFX FXML
Zaczynamy od utworzenia w NetBeans nowego projektu, wybierając tym razem w okienku „JavaFX FXML Application”.
Nadajemy projektowi nazwę AplikacjaFXML
i zatwierdzamy.
Zauważ, że teraz zostały w pakiecie utworzone trzy pliki:
Można uruchomić program, pojawi się okienko z przyciskiem:
Po naciśnięciu przycisku pojawi się napis:
Z poprzednich lekcji poświęconych JavieFX zapewne wiesz wystarczająco dużo, żeby się domyślić jaka jest struktura tej aplikacji. Przyjrzyjmy się jednak w jaki sposób taki rezultat został osiągnięty z użyciem pliku FXML. Zacznijmy od analizy trzech plików:
AplikacjaFXML.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package aplikacjafxml; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class AplikacjaFXML extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml")); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } } |
Jak widać w tym pliku znajdują się metody main()
i start()
. Ich zawartość powinna być zrozumiała, nowością jest w zasadzie tyko linia:
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Można jednak się domyślić, że chodzi tu o pobranie struktury interfejsu graficznego z pliku o nazwie FXMLDocument.fxml
, który znajduje się w naszym projekcie.
FXMLDocument.fxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="UTF-8"?> <?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="aplikacjafxml.FXMLDocumentController"> <children> <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" /> <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" /> </children> </AnchorPane> |
Jeśli miałeś dotychczas do czynienia z plikami XML
, na przykład z HTML
struktura powyższego pliku będzie z grubsza zrozumiała. Nie będę tu omawiał jej dokładnie, zwrócę jedynie uwagę na kilka charakterystycznych cech tego typu plików. Zauważ, że występują w nim znaczniki ograniczone znakami <
i >
. Para znaczników wyznacza początek i koniec elementu. Znacznik otwierający go wygląda tak: <element>
a zamykający tak: </element>
. W powyższym pliku taką parą jest np. <children>
i </children>
. W znaczniku otwierającym mogą się także znaleźć atrybuty: <element nazwaAtrybutu="Wartość atrybutu">
. Dobrym przykładem jest znacznik otwierający element AnchorPane
, który zawiera szereg atrybutów:
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="aplikacjafxml.FXMLDocumentController">
Przy okazji nie trudno zauważyć, że element ten odpowiada panelowi AnchorPane
a atrybuty wyznaczają jego cechy, jak identyfikator, czy preferowane rozmiary. Znajduje się tam także odwołanie do trzeciego pliku w naszej aplikacji: fx:controller="aplikacjafxml.FXMLDocumentController"
ale do tego wrócimy za chwilę. Element AnchorPane
obejmuje, albo jak w takich sytuacjach mówimy, jest rodzicem, elementu children
a ten z kolei zawiera dwa kolejne elementy odpowiadające kontrolkom Button
i Label
. Zauważ, że w obu przypadkach mamy do czynienia nie z parami znaczników, ale z pojedynczym znacznikiem, zawierającym znak /
. To jest tak zwany "znacznik pusty", co oznacza, że nie ma "dziecka" czyli, że nie obejmuje elementu niższego rzędu. Tak więc znacznik <element />
odpowiada parze znaczników: <element></element>
.
Elementy odpowiadające dwóm kontrolkom także zawierają serie atrybutów odpowiadających za ich położenie, wyświetlany tekst etc.. Interesujący jest zwłaszcza jeden, przypisany przyciskowi: onAction="#handleButtonAction"
, odpowiada on metodzie wywoływanej przy wciśnięciu przycisku. Nie było tej metody w pierwszym z omawianych plików, zatem znajdziemy ją za chwilę w trzecim.
Zwróć teraz uwagę na górną część pliku:
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
...
Znów, nie jest trudno wpaść na ich przeznaczenie. Odpowiadają one za import odpowiednich klas. Zauważ, że o ile w plikach z kodem Javy nie musieliśmy importować klas z pakietu java.lang
, w przypadku plików FXML
jest to konieczne.
FXMLDocumentController.java
To jest plik kontrolera (jak sama nazwa wskazuje) czyli plik zawierający kod obsługujący elementy interfejsu graficznego zdefiniowanego w pliku FXML
. Przypominam, że w poprzednio omawianym pliku FXMLDocument.fxml
znajduje się odwołanie do pliku FXMLDocumentController.java
w elemencie odpowiadającym panelowi AnchorPane
, które jasno wskazuje gdzie szukać właściwego kodu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package aplikacjafxml; import java.net.URL; import java.util.ResourceBundle; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.Label; public class FXMLDocumentController implements Initializable { @FXML private Label label; @FXML private void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); label.setText("Hello World!"); } @Override public void initialize(URL url, ResourceBundle rb) { // TODO } } |
Jak widać plik wygląda jak "normalny" plik z kodem Javy, zwraca jedynie uwagę mnogość adnotacji "@FXML", które wskazują na elementy interfejsu zdefiniowane w pliku FXML
a także na metody, które będą przez nie wykorzystywane. Tu jest jedna taka metoda, handleButtonAction()
, do której odwołanie, jak pamiętamy, znalazło się w znaczniku odpowiadającemu przyciskowi.
Małe modyfikacje
Spróbujmy teraz zrobić małą modyfikację, podobną do tej jaką wykonaliśmy przy okazji naszej pierwszej aplikacji w JavieFX, czyli dołożymy do interfejsu czerwone koło. Dopisz zatem do pliku FXMLDocument.fxml
, zaraz po znaczniku dla kontrolki Label
znacznik dla koła
<Circle layoutX="162" layoutY="150" radius="30" fill="red" fx:id="kolko" />
Nie zapomnij też zaimportować odpowiedniej klasy:
<?import javafx.scene.shape.Circle?>
Teraz, zgodnie z oczekiwaniami pojawia się czerwone kółko:
Spróbuj teraz pozmieniać parametry kontrolek, obserwując wpływ zmian na wygląd interfejsu.
Pobranie i instalacja Scene Buildera
Firma Oracle jakiś czas temu zaprzestała udostępniania nowszych wersji Scene Buildera. Na szczęście, inicjatywę przejęła firma Gluon, która obecnie udostępnia odpowiednie pliki instalacyjne a także plik .jar
tutaj. Możemy pobrać plik instalacyjny odpowiedni dla używanego systemu albo jeśli nie chcemy instalować programu, wybieramy plik .jar
, który umieszczamy w wygodnym miejscu.
Po instalacji uruchom program, pojawi się takie okno:
Możesz zamknąć program.
Używamy Scene Buildera
Teraz kliknij prawym klawiszem myszy na pliku FXMLDocument.fxml
w ramce "Projects", powinno się pojawić menu a w nim pozycja Open Jeśli jej nie ma a zainstalowałeś Scene Buildera to trzeba NetBeans wskazać, gdzie się on znajduje. W tym celu wejdź w menu NetBeans -> Preferences…. W oknie, które się pokaże wybierz zakładkę JavaFX. Zaznacz Save all modified files… i wybierz miejsce zainstalowania Scene Buildera.
Niestety, może się okazać, że otrzymasz taki komunikat:
Przyczyną jest zapewne niedostosowanie NetBeans do plików rozpowszechnionych przez firmę Gluon.
Udało mi się znaleźć w internecie obejście tego problemu dla systemu Windows tutaj, ale nie próbowałem go zastosować. Jeśli ktoś ma inny pomysł proszę śmiało pisać w komentarzach.
Innym rozwiązaniem, które sam zastosowałem, jest pobranie i zainstalowanie developerskiej wersji NetBeans ze strony: http://bits.netbeans.org/dev/nightly/, gdzie wybieramy najnowszą wersję oprogramowania (wg. daty). Trzeba jednak pamiętać, że taka wersja najprawdopodobniej nie jest całkiem stabilna. Miejmy nadzieję że w kolejnej stabilnej wersji NetBeans wszystko już będzie działało bez zakłóceń.
Jeśli tym razem po kliknięciu prawym klawiszem na plik FXMLDocument.fxml
w menu pojawiła się opcja Open, to wybierz ją. Teraz zresztą podwójne kliknięcie na tym pliku powinno otworzyć go domyślnie w Scene Builderze. Jeśli chcesz go edytować tradycyjnie należy z menu wybrać Edit. Powinieneś zobaczyć taki widok:
Jak widać znajduje się tam interfejs graficzny naszej aplikacji, przy czym jest on edytowalny. Kliknij na przycisku. Teraz okno wygląda tak:
Po lewej stronie u góry znajduje się paleta kontrolek i paneli. Poniżej widać strukturę aplikacji. W centralnej części widać projekt interfejsu. Po lewej mamy trzy zakładki w których możemy edytować właściwości, ułożenie i kod związany z obecnie zaznaczonym elementem interfejsu, teraz jest to kontrolka Button
. Przyjrzyj się im i spróbuj poeksperymentować zmieniając ich wartości. Zauważ, że można też edytować takie rzeczy jak rozmiary czy położenie elementu, bezpośrednio w części centralnej aplikacji odpowiednio przeciągając krawędzie czy całe kontrolki.
Jeśli teraz zapiszesz plik (menu: File -> Save) to w NetBeans możesz zauważyć, że kod w pliku FXMLDocument.fxml
również się zmienił.
Wróć teraz do Scene Buildera, wybierz z lewej strony zakładkę Controls i znajdź tam kontrolkę TextField
. Przeciągnij ją na projekt interfejsu.
Dostosuj jej rozmiar, a następnie w Properties po prawej najpierw ustaw wartość pola Prompt Text na "wpisz coś" a pola Id (poniżej) na "poleTextowe".
Teraz jeszcze trzeba ustawić odpowiednie parametry kontrolki Label
, która nie jest widoczna na projekcie interfejsu, ale możemy ją łatwo znaleźć w oknie hierarchii dokumentu (po lewej na dole, klikając w odpowiednią pozycję. Teraz kontrolka będzie zaznaczona.
Możemy ją ułożyć bardziej sensownie, zmienić wymiary, a przy okazji przesunąć trochę koło.
Zapisz plik. Teraz wygląda on tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.text.*?> <?import javafx.scene.shape.*?> <?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.shape.Circle?> <AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="aplikacjafxml.FXMLDocumentController"> <children> <Button fx:id="button" layoutX="171.0" layoutY="158.0" onAction="#handleButtonAction" prefHeight="28.0" prefWidth="125.0" text="Click Me!" textFill="#115b0c"> <font> <Font name="DejaVu Serif Bold" size="14.0" /> </font></Button> <Label fx:id="label" layoutX="20.0" layoutY="62.0" minHeight="16" minWidth="69" prefHeight="26.0" prefWidth="276.0" /> <Circle fx:id="kolko" fill="red" layoutX="94.0" layoutY="142.0" radius="30" /> <TextField id="poleTekstowe" layoutX="20.0" layoutY="26.0" prefHeight="26.0" prefWidth="276.0" promptText="wpisz coś" /> </children> </AnchorPane> |
Zauważ, że pojawił się element font
zagnieżdżony w elemencie Button
(zmieniłem czcionkę przycisku) a także element odpowiadający kontrolce TextField
. Zwróć uwagę na atrybut id
: id="poleTekstowe"
. Uwaga, należy go zmienić tak: fx:id="poleTekstowe"
.
Można też było od razu ustawić wartość fx:id
w SceneBuilderze w zakłądce Code
inspektora.
Teraz zajmijmy się edycją pliku FXMLDocumentController.java
.
Dopisz:
@FXML
private TextField poleTekstowe;
@FXML
private Circle kolko;
Do importów dodaj:
import javafx.scene.control.TextField;
import javafx.scene.shape.Circle;
A metodę handleButtonAction()
zmodyfikuj w ten sposób:
private void handleButtonAction(ActionEvent event) {
label.setText(poleTekstowe.getText());
kolko.setFill(Color.GREEN);
}
Teraz po uruchomieniu programu możemy coś wpisać do pola tekstowego i kliknąć na przycisk, efekt może wyglądać tak:
Leave a Reply
You must be logged in to post a comment.