Setelah Swing, mari berpindah ke toolkit yang dirancang satu dekade kemudian dengan pelajaran dari Swing sudah terserap: JavaFX. JavaFX mengganti metafora jendela-dan-panel dengan panggung teater — Stage adalah jendela, Scene adalah isi yang sedang dipentaskan, dan Node adalah setiap elemen di atasnya: label, tombol, panel tata letak, hingga bentuk dan efek grafis. Seluruh UI adalah pohon node yang bisa diganti seluruhnya hanya dengan menukar Scene milik Stage. Chapter ini mengenal siklus hidup aplikasi JavaFX, menyusun pohon scene graph, dan memetakan padanan konsepnya dengan Swing yang baru saja kita kuasai.
Menyiapkan JavaFX pada JDK 25 #
Karena JavaFX keluar dari JDK sejak versi 11, lingkungan kerja kita — JDK 25 murni — tidak memuat javafx.*. Ada dua jalur resmi untuk menggunakannya. Jalur pertama adalah dependensi Maven/Gradle: deklarasikan org.openjfx:javafx-controls, javafx.graphics, dan javafx.base (versi 25 sesuai JDK kita), lalu biarkan build tool mengunduh modul-modul tersebut. Jalur kedua adalah SDK terpisah dari openjfx.io
: unduh arsip berisi modul javafx.*, lalu jalankan dengan --module-path dan --add-modules. Seluruh kode JavaFX pada chapter ini ditampilkan sebagai contoh bacaan yang bisa Anda jalankan pada lingkungan dengan JavaFX terpasang — bukan sebagai program yang dieksekusi oleh buku ini.
java --module-path /path/ke/javafx-sdk-25/lib \
--add-modules javafx.controls,javafx.graphics \
HaloJavaFX.javaSiklus Hidup Application Stage dan Scene #
Setiap program JavaFX dimulai dari kelas yang memperluas javafx.application.Application. Method launch(...) milik JavaFX memulai toolkit: ia membuat thread JavaFX Application Thread, memanggil init(), lalu start(Stage) sebagai tempat kita menyusun UI, dan stop() saat aplikasi ditutup. Berikut aplikasi lengkap pertama kita:
import javafx.application.Application;
import javafx.geometry.Pos;
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 HaloJavaFX extends Application {
@Override
public void start(Stage panggung) {
panggung.setTitle("Aplikasi Data Siswa");
Label label = new Label("Halo, Budi!");
Button tombol = new Button("Simpan");
tombol.setOnAction(e -> label.setText("Data Budi tersimpan"));
VBox akar = new VBox(10, label, tombol);
akar.setAlignment(Pos.CENTER);
Scene skena = new Scene(akar, 360, 200);
panggung.setScene(skena);
panggung.show();
}
public static void main(String[] args) {
launch(args);
}
}Mari kita telaah baris-baris pentingnya:
- Baris 12 (
extends Application): kelas aplikasi kita menyerahkan kendali hidupnya kepada JavaFX. Methodlaunchpadamainmembuat instance kelas ini lewat konstruktor tanpa argumen, memulai thread JavaFX, lalu memanggil method siklus hidup secara berurutan. - Baris 14 (
start(Stage panggung)): parameterStageadalah jendela utama yang disiapkan JavaFX untuk kita — bandingkan dengan Swing yang membuatJFramesendiri lewatnew. Kita tidak membuatStagedengannewuntuk jendela utama. - Baris 22 (
new VBox(10, label, tombol)):VBoxadalah panel tata letak yang menyusun anak-anaknya vertikal dengan jarak 10 piksel. Perhatikan bahwa anak dimasukkan langsung lewat konstruktor — idiom JavaFX yang ringkas. - Baris 26 (
new Scene(akar, 360, 200)):Scenemembungkus satu pohon node beserta ukurannya. SebuahStagehanya punya satuSceneaktif; menukarpanggung.setScene(...)mengganti seluruh isi jendela sekaligus — teknik berpindah halaman yang lazim. - Baris 28 (
panggung.show()): padanansetVisible(true)pada Swing. Setelah baris ini, thread JavaFX Application Thread menjaga aplikasi tetap hidup — peran yang di Swing dipegang EDT.
Konsep penjadwalan thread juga paralel: sama seperti komponen Swing, node JavaFX hanya boleh disentuh dari thread khususnya (JavaFX Application Thread). Perbedaan mencoloknya di tempat lain — JavaFX menyediakan properti yang bisa diikat dan diperbarui otomatis, topik Chapter JavaFX Properties dan Bindings.
Pohon Node dan Struktur Data Skena #
Sebelum membaca kode JavaFX lebih jauh, mari kita pegang struktur intinya dengan tangan sendiri. Pohon scene graph pada contoh di atas berbentuk: Scene berisi VBox, yang berisi Label, TextField, dan HBox berisi dua Button. Program berikut membangun pohon yang sama memakai kelas Java biasa lalu menelusurinya — bukti bahwa scene graph pada dasarnya adalah struktur data rekursif yang bisa dipelajari tanpa JavaFX terpasang:
import java.util.ArrayList;
import java.util.List;
/** Simulasi pohon scene graph JavaFX dalam kelas Java biasa. */
public class PohonSkena {
/** Satu node dalam pohon: punya nama dan daftar anak. */
static class Node {
final String nama;
final List<Node> anak = new ArrayList<>();
Node(String nama) {
this.nama = nama;
}
Node tambahAnak(Node n) {
anak.add(n);
return this;
}
}
public static void main(String[] args) {
Node akar = new Node("Scene");
akar.tambahAnak(new Node("VBox"));
akar.anak.get(0).tambahAnak(new Node("Label"));
akar.anak.get(0).tambahAnak(new Node("TextField"));
Node tombolBaris = new Node("HBox");
tombolBaris.tambahAnak(new Node("Button"));
tombolBaris.tambahAnak(new Node("Button"));
akar.anak.get(0).tambahAnak(tombolBaris);
cetak(akar, 0);
System.out.println("jumlah node = " + hitung(akar));
System.out.println("kedalaman = " + kedalaman(akar));
}
static void cetak(Node n, int tingkat) {
System.out.println(" ".repeat(tingkat) + n.nama);
for (Node a : n.anak) {
cetak(a, tingkat + 1);
}
}
static int hitung(Node n) {
int total = 1;
for (Node a : n.anak) {
total += hitung(a);
}
return total;
}
static int kedalaman(Node n) {
int dalam = 0;
for (Node a : n.anak) {
dalam = Math.max(dalam, kedalaman(a) + 1);
}
return dalam;
}
}Output yang Dihasilkan:
Scene
VBox
Label
TextField
HBox
Button
Button
jumlah node = 7
kedalaman = 3Dua fungsi rekursif di ujung program menunjukkan kegunaan struktur pohon: hitung menjumlahkan diri sendiri ditambah seluruh keturunannya (hasil 7 — satu Scene, satu VBox, satu HBox, Label, TextField, dan dua Button), sedangkan kedalaman mengukur rantai anak terpanjang (hasil 3 — dari Scene turun ke VBox lalu ke anak-anaknya). JavaFX sendiri melakukan penelusuran seribu kali lipat lebih rumit setiap frame: menghitung ukuran tiap node, menyusun tata letak, lalu merender pohon ke layar — semuanya dengan berbekal struktur getParent() dan getChildren() seperti yang kita tiru di sini.
Padanan Konsep Swing dan JavaFX #
Pemetaan berikut mempercepat migrasi pengetahuan dari satu toolkit ke toolkit lain:
| Konsep | Swing | JavaFX |
|---|---|---|
| Thread UI | Event Dispatch Thread | JavaFX Application Thread |
| Jendela | JFrame (kita buat sendiri) | Stage (disiapkan start) |
| Isi jendela | content pane JFrame | Scene (bisa ditukar utuh) |
| Wadah tata letak | JPanel + layout manager | Pane turunan (VBox, BorderPane) |
| Komponen | JComponent turunan | Node turunan (Control, bentuk, media) |
| Klik tombol | addActionListener | setOnAction |
| Pekerjaan latar | SwingWorker | Task dan Service |
Satu perbedaan struktural menonjol: pada Swing, layout manager adalah objek terpisah yang dipasang ke panel, sedangkan pada JavaFX panel tata letak (VBox, HBox, BorderPane, GridPane, StackPane) adalah node itu sendiri. Pohon di tangan dan peta konsep di saku, kita siap melangkah lebih jauh: menulis antarmuka secara deklaratif lewat FXML dan merancangnya secara visual di Scene Builder — topik Chapter berikutnya.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
PohonSkena.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
/** Simulasi pohon scene graph JavaFX dalam kelas Java biasa. */
public class PohonSkena {
/** Satu node dalam pohon: punya nama dan daftar anak. */
static class Node {
final String nama;
final List<Node> anak = new ArrayList<>();
Node(String nama) {
this.nama = nama;
}
Node tambahAnak(Node n) {
anak.add(n);
return this;
}
}
public static void main(String[] args) {
Node akar = new Node("Scene");
akar.tambahAnak(new Node("VBox"));
akar.anak.get(0).tambahAnak(new Node("Label"));
akar.anak.get(0).tambahAnak(new Node("TextField"));
Node tombolBaris = new Node("HBox");
tombolBaris.tambahAnak(new Node("Button"));
tombolBaris.tambahAnak(new Node("Button"));
akar.anak.get(0).tambahAnak(tombolBaris);
cetak(akar, 0);
System.out.println("jumlah node = " + hitung(akar));
System.out.println("kedalaman = " + kedalaman(akar));
}
static void cetak(Node n, int tingkat) {
System.out.println(" ".repeat(tingkat) + n.nama);
for (Node a : n.anak) {
cetak(a, tingkat + 1);
}
}
static int hitung(Node n) {
int total = 1;
for (Node a : n.anak) {
total += hitung(a);
}
return total;
}
static int kedalaman(Node n) {
int dalam = 0;
for (Node a : n.anak) {
dalam = Math.max(dalam, kedalaman(a) + 1);
}
return dalam;
}
}