Swing menyusun setiap aplikasi dari tiga lapis benda: jendela (JFrame) sebagai wadah terluar, panel (JPanel) sebagai permukaan pengatur letak, dan komponen (JButton, JLabel, JTextField) sebagai penyaji data serta pintu interaksi pengguna. Ketiganya membentuk pohon yang kita bangun lewat method add, persis seperti contoh pohon kontainer pada Chapter sebelumnya. Ada satu aturan rahasia yang membedakan Swing dari program konsol biasa: seluruh komponen hidup di satu thread khusus bernama Event Dispatch Thread (EDT), dan melanggar aturan tersebut adalah sumber bug paling umum pemula. Chapter ini membahas ketiganya — hierarki, komponen dasar, dan EDT — sambil membangun fondasi bagi seluruh chapter Swing berikutnya.
Hierarki JFrame JPanel dan Komponen #
Sebuah jendela Swing yang paling sederhana tampak seperti berikut. Kode ini membuka jendela nyata, jadi kita baca strukturnya tanpa mengeksekusinya di lingkungan tanpa layar:
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
public class HaloDuniaSwing {
public static void main(String[] args) {
JFrame jendela = new JFrame("Aplikasi Data Siswa");
jendela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jendela.setSize(360, 180);
JLabel judul = new JLabel("Halo, Budi!", SwingConstants.CENTER);
jendela.add(judul, BorderLayout.CENTER);
JPanel panelTombol = new JPanel();
JButton simpan = new JButton("Simpan");
panelTombol.add(simpan);
jendela.add(panelTombol, BorderLayout.SOUTH);
jendela.setVisible(true);
}
}Mari kita telaah baris-baris pentingnya:
- Baris 10 (
new JFrame(...)): konstruktor menerima teks yang tampil pada bilah judul jendela. JFrame adalah komponen heavyweight — satu-satunya bagian Swing yang benar-benar jendela sistem operasi. - Baris 11 (
setDefaultCloseOperation): menentukan perilaku saat tombol tutup jendela diklik.EXIT_ON_CLOSEmematikan seluruh JVM; tanpa baris ini jendela tertutup tetapi program tetap berjalan. - Baris 15 dan 18 (
jendela.add(...)): komponen dan panel disematkan langsung ke content pane milik JFrame. Argumen kedua (BorderLayout.CENTER,BorderLayout.SOUTH) memilih wilayah penempatan — pembahasan penuh ada pada Chapter Layouts dan Components. - Baris 19 (
panelTombol.add(simpan)): tombol diletakkan di dalam panel, bukan langsung di jendela. Panel adalah lapis perantara yang membuat penataan komponen fleksibel. - Baris 22 (
setVisible(true)): baris ini membuka jendela nyata di layar. Setelah dipanggil, thread utama boleh selesai — EDT mengambil alih dan menjaga aplikasi tetap hidup.
Perhatikan urutan pembangunan: susun pohon dulu, tampilkan paling akhir. Membuka jendela sebelum pohon rampung membuat pengguna menyaksikan antarmuka yang setengah jadi.
Komponen Dasar Swing #
Keluarga komponen Swing berawalan J dan seluruhnya kelas ringan (lightweight) yang digambar oleh Java sendiri. Tabel berikut merangkum yang paling sering dipakai:
| Komponen | Peran | API kunci |
|---|---|---|
JLabel | teks/ikon statis | getText(), setIcon(...) |
JButton | pemicu aksi | addActionListener(...), setEnabled(false) |
JTextField | input satu baris | getText(), setText(...) |
JTextArea | input banyak baris | append(...), setLineWrap(true) |
JCheckBox | pilihan benar/salah | isSelected(), setSelected(true) |
JComboBox | pilihan dari daftar | getSelectedItem(), addItem(...) |
Seluruh komponen tersebut mewarisi javax.swing.JComponent, sehingga berbagi API dasar: setEnabled(...) untuk mengaktifkan/menonaktifkan, setToolTipText(...) untuk tooltip, dan setVisible(...) untuk menyembunyikan. Karena komponen ringan tidak membuka jendela sistem operasi, kita bisa mengujinya tanpa layar. Program berikut membangun panel formulir dan memeriksa perilaku API-nya:
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/** Menguji komponen dasar Swing tanpa membuka jendela nyata. */
public class KomponenDasar {
public static void main(String[] args) {
JLabel label = new JLabel("Nama siswa:");
JTextField input = new JTextField("Budi", 12);
JButton simpan = new JButton("Simpan");
JButton batal = new JButton("Batal");
simpan.setToolTipText("Simpan data ke daftar siswa");
batal.setEnabled(false);
JPanel panel = new JPanel();
panel.add(label);
panel.add(input);
panel.add(simpan);
panel.add(batal);
System.out.println("jumlah anak panel = " + panel.getComponentCount());
System.out.println("teks label = " + label.getText());
System.out.println("isi input = " + input.getText());
System.out.println("tooltip = " + simpan.getToolTipText());
System.out.println("aktif simpan = " + simpan.isEnabled());
System.out.println("aktif batal = " + batal.isEnabled());
input.setText("Siti");
System.out.println("isi input setelah diubah = " + input.getText());
}
}Output yang Dihasilkan:
jumlah anak panel = 4
teks label = Nama siswa:
isi input = Budi
tooltip = Simpan data ke daftar siswa
aktif simpan = true
aktif batal = false
isi input setelah diubah = SitiBeberapa detail patut dicermati dari hasilnya. Method getComponentCount() melaporkan empat anak karena empat pemanggilan add dilakukan berurutan. Method setEnabled(false) pada tombol Batal tidak menghapus tombol — tombol tetap ada di pohon dan terhitung, tetapi berstatus abu-abu dan tidak menerima klik. Perubahan teks lewat setText("Siti") langsung terbaca kembali lewat getText(), membuktikan bahwa state komponen disimpan pada objek Java biasa yang bisa kita baca-ulang kapan pun.
Event Dispatch Thread #
Swing bukan program berurutan yang menunggu input seperti Scanner.nextLine(). Setelah jendela terbuka, Swing memasuki loop kejadian: EDT mengambil kejadian dari antrean (klik, ketikan, penggeseran mouse) dan mengirimkannya ke listener yang terpasang. EDT juga satu-satunya thread yang boleh menyentuh komponen. Aturan ini lahir dari alasan nyata: komponen Swing tidak disinkronkan dengan lock, sehingga dua thread yang mengubah pohon secara bersamaan bisa membuat data komponen saling menimpa.
Program berikut membuktikan bahwa thread utama (main) bukan EDT, lalu berpindah ke EDT memakai SwingUtilities.invokeAndWait:
import javax.swing.SwingUtilities;
/** Menunjukkan thread utama bukan EDT, dan cara berpindah ke EDT. */
public class EdtDemo {
public static void main(String[] args) throws Exception {
System.out.println("di main : EDT? " + SwingUtilities.isEventDispatchThread());
SwingUtilities.invokeAndWait(() -> {
System.out.println("di runnable : EDT? " + SwingUtilities.isEventDispatchThread());
});
System.out.println("kembali main : EDT? " + SwingUtilities.isEventDispatchThread());
}
}Output yang Dihasilkan:
di main : EDT? false
di runnable : EDT? true
kembali main : EDT? falseMari kita bedah tiga baris intinya:
- Baris 6 (
SwingUtilities.isEventDispatchThread()): method ini bertanya “apakah thread yang sedang menjalankan kode ini adalah EDT?” — bukan “apakah EDT ada?”. EDT diciptakan otomatis oleh toolkit saat pertama kali dibutuhkan. - Baris 8 (
invokeAndWait(...)): meletakkanRunnableke antrean EDT lalu menunggu hingga selesai dieksekusi, sehingga baris 11 berjalan setelah runnable tuntas. Karena runnable dieksekusi oleh EDT, hasil pemeriksaan di dalamnya menjaditrue. - Baris 11 (cetak setelah invokeAndWait): thread utama melanjutkan hidupnya sendiri — kembali
false. Dua thread berbagi satu program, dan hanya salah satunya yang boleh menyentuh komponen.
Pola Aplikasi yang Benar #
Menggabungkan seluruh aturan, kerangka aplikasi Swing yang benar membungkus pembangunan UI di dalam invokeLater, sehingga pohon komponen dibangun oleh EDT sejak awal:
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class PolaAman {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> bangunUi());
}
private static void bangunUi() {
JFrame jendela = new JFrame("Aplikasi Aman");
jendela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jendela.add(new JLabel("Dibangun di dalam EDT", JLabel.CENTER));
jendela.add(new JButton("OK"), java.awt.BorderLayout.SOUTH);
jendela.pack(); // ukur komponen lalu rapatkan jendela
jendela.setLocationRelativeTo(null); // tampil di tengah layar
jendela.setVisible(true);
}
}Method pack() menghitung ukuran jendela dari ukuran pilihan (preferred size) seluruh komponen — lebih baik daripada setSize manual yang mudah kekecilan saat teks panjang. Dengan kerangka ini, kita siap mengatur komponen secara terstruktur. Chapter berikutnya menyelami layout manager: tangan tak terlihat yang menentukan posisi dan ukuran setiap komponen di dalam panel.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
EdtDemo.java
Unduh berkasimport javax.swing.SwingUtilities;
/** Menunjukkan thread utama bukan EDT, dan cara berpindah ke EDT. */
public class EdtDemo {
public static void main(String[] args) throws Exception {
System.out.println("di main : EDT? " + SwingUtilities.isEventDispatchThread());
SwingUtilities.invokeAndWait(() -> {
System.out.println("di runnable : EDT? " + SwingUtilities.isEventDispatchThread());
});
System.out.println("kembali main : EDT? " + SwingUtilities.isEventDispatchThread());
}
}
KomponenDasar.java
Unduh berkasimport javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/** Menguji komponen dasar Swing tanpa membuka jendela nyata. */
public class KomponenDasar {
public static void main(String[] args) {
JLabel label = new JLabel("Nama siswa:");
JTextField input = new JTextField("Budi", 12);
JButton simpan = new JButton("Simpan");
JButton batal = new JButton("Batal");
simpan.setToolTipText("Simpan data ke daftar siswa");
batal.setEnabled(false);
JPanel panel = new JPanel();
panel.add(label);
panel.add(input);
panel.add(simpan);
panel.add(batal);
System.out.println("jumlah anak panel = " + panel.getComponentCount());
System.out.println("teks label = " + label.getText());
System.out.println("isi input = " + input.getText());
System.out.println("tooltip = " + simpan.getToolTipText());
System.out.println("aktif simpan = " + simpan.isEnabled());
System.out.println("aktif batal = " + batal.isEnabled());
input.setText("Siti");
System.out.println("isi input setelah diubah = " + input.getText());
}
}