SEN1002 · 5. hafta · GUI Components

GUI bileşenleri: JFrame, JLabel, JTextField, JButton ve layout'lar

Swing ile pencere kurmak: bileşenler, içerik paneli ve FlowLayout, BorderLayout, GridLayout. Sınavda kod okuma ve 'hangi layout' soruları.

GUISwingJFrameJLabelJTextFieldJPasswordFieldJButtonJPanellayout managerFlowLayoutBorderLayoutGridLayoutcontent panecomponentcontainer

Bu hafta grafik arayüzün (graphical user interface, GUI) parçalarını kuruyoruz: pencere, yazı, metin kutusu, buton ve bunları yerleştiren layout’lar. Tıklamaya tepki vermek (event handling) gelecek haftanın konusu. Sınavda bu konu genelde kod okuma olarak gelir: “bu pencerede ne görünür”, “hangi layout”, “bu satır neden derlenmiyor”. Ezberlemen gereken çok şey yok; birkaç varsayılanı ve layout’ların davranışını bilmen yeterli.

Altın kurallar:

  1. Her şey String. JTextField.getText() String döner; sayı istiyorsan Integer.parseInt.
  2. JPanel varsayılanı FlowLayout, JFrame’in içerik paneli BorderLayout.
  3. BorderLayout’ta her bölgeye tek bileşen. Konumsuz add CENTER’a gider, öncekini ezer.
  4. setVisible(true) en son. Kapatınca programın bitmesi için EXIT_ON_CLOSE.

İPUCU · Swing mi JavaFX mi? Deitel’in kitabı Swing ile anlatır (yeni baskılarda JavaFX bölümleri de var). Bu sayfa Swing kullanıyor; JDK ile birlikte gelir. Hocan JavaFX gösteriyorsa kavramlar birebir aynı: Stage ≈ JFrame, Scene ≈ içerik paneli, Label / TextField / Button, layout yerine HBox, VBox, GridPane, BorderPane.

Swing’in yapısı

GUI’deki her parça bir bileşendir (component). Bileşenleri tutan bileşenlere kap (container) denir. JFrame en dıştaki pencere, JPanel ise içine bileşen koyduğun ve pencerenin içine yerleştirdiğin bir kap.

SınıfNe işe yarar?Sık metotlar
JFrameBaşlıklı, kapatılabilir penceresetSize, pack, setVisible, setLayout, add, setDefaultCloseOperation
JLabelDeğiştirilemez yazı (ya da ikon)setText, getText
JTextFieldTek satırlık metin girişigetText, setText, setEditable
JPasswordFieldYıldızlı şifre girişigetPassword (char[] döner)
JTextAreaÇok satırlı metinappend, getText
JButtonTıklanabilir butonsetText, addActionListener (6. hafta)
JCheckBox, JRadioButtonSeçim kutularıisSelected
JPanelBileşen grubu tutan kapadd, setLayout

Kalıtım zinciri sınavda “is-a” sorusu olarak çıkabilir:

java.awt.Component
  └─ java.awt.Container
       ├─ java.awt.Window ─ java.awt.Frame ─ javax.swing.JFrame
       └─ javax.swing.JComponent
            ├─ JLabel
            ├─ JPanel
            ├─ JTextComponent ─ JTextField ─ JPasswordField
            └─ AbstractButton ─ JButton
                              └─ JToggleButton ─ JCheckBox, JRadioButton
JButton jb = new JButton("OK");
System.out.println(jb instanceof JComponent);
System.out.println(jb instanceof Container);
System.out.println(jb instanceof Component);
Object lbl = new JLabel();
System.out.println(lbl instanceof AbstractButton);
System.out.println(new JCheckBox() instanceof AbstractButton);
true
true
true
false
true

İlk pencere

Pencere yazmanın standart yolu JFrame’den türeyen bir sınıf açmak ve bileşenleri constructor’da kurmaktır. Bileşenleri alan olarak tutarsan event handler’lar (6. hafta) onlara erişebilir.

import java.awt.*;
import javax.swing.*;

public class LoginFrame extends JFrame {
    private final JLabel userLabel = new JLabel("User:");
    private final JTextField userField = new JTextField(12);
    private final JLabel passLabel = new JLabel("Password:");
    private final JPasswordField passField = new JPasswordField(12);
    private final JButton loginButton = new JButton("Login");

    public LoginFrame() {
        super("Login");                         // pencere başlığı
        setLayout(new FlowLayout());            // varsayılan BorderLayout'u değiştir
        add(userLabel);
        add(userField);
        add(passLabel);
        add(passField);
        add(loginButton);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(260, 140);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new LoginFrame().setVisible(true));
    }
}

SwingUtilities.invokeLater pencereyi Swing’in kendi iş parçacığında (event dispatch thread) kurar; kitaplardaki doğru kalıp budur. Sınavda new LoginFrame().setVisible(true); yazman da kabul görür.

TUZAK · kapanınca program bitmiyor. JFrame’in varsayılan kapanış davranışı HIDE_ON_CLOSE: pencere görünmez olur ama JVM çalışmaya devam eder. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) yazmayı unutma.

Metin kutusundan değer okumak

Kullanıcının yazdığı her şey String olarak gelir. Sayısal işlem için dönüştürmen gerekir; dönüştürülemeyen metin NumberFormatException fırlatır.

JTextField field = new JTextField(10);
System.out.println("[" + field.getText() + "] cols=" + field.getColumns());
field.setText("Deniz Yilmaz Uzun Bir Isim");
System.out.println(field.getText().length());     // 10 sütun bir sınır değil

JTextField num = new JTextField("12");
int n = Integer.parseInt(num.getText());
System.out.println(n * 2);
num.setText("12a");
try {
    Integer.parseInt(num.getText());
} catch (NumberFormatException e) {
    System.out.println("NumberFormatException: " + e.getMessage());
}
[] cols=10
26
24
NumberFormatException: For input string: "12a"

Layout yöneticileri

Swing’de bileşenleri piksel piksel konumlandırmazsın. Her kabın bir layout yöneticisi (layout manager) vardır; bileşenlerin nereye ve hangi boyutta oturacağına o karar verir. Pencere büyüyüp küçüldüğünde yerleşimi yeniden hesaplar.

LayoutYerleşimVarsayılan olduğu yerEkleme
FlowLayoutSoldan sağa sıra, satır dolunca alta; bileşen kendi tercih ettiği boyuttaJPaneladd(c)
BorderLayout5 bölge: NORTH, SOUTH, EAST, WEST, CENTER; CENTER kalan alanı doldururJFrame içerik paneliadd(c, BorderLayout.SOUTH)
GridLayoutEşit boyutlu hücrelerden ızgara, soldan sağa yukarıdan aşağıyokadd(c)
JPanel panel = new JPanel();
System.out.println(panel.getLayout().getClass().getSimpleName());
JRootPane root = new JRootPane();      // JFrame'in içerik panelini kuran yapı
System.out.println(root.getContentPane().getLayout() instanceof BorderLayout);
FlowLayout
true

FlowLayout

En basiti. Bileşenler eklenme sırasıyla yan yana dizilir, varsayılan olarak ortalanır. new FlowLayout(FlowLayout.LEFT) sola yaslar. Pencere daralırsa sığmayan bileşen alt satıra kayar. Formlar için pratik ama düzen pencere boyutuna göre değiştiği için hassas tasarımda yetersiz.

BorderLayout

Pencereyi beş bölgeye ayırır. Kuzey ve güney tam genişlik alır, doğu ve batı kalan yüksekliği, merkez geri kalan her şeyi. Bölge vermezsen add(c) merkeze ekler.

JPanel bp = new JPanel(new BorderLayout());
JButton b1 = new JButton("One"), b2 = new JButton("Two");
bp.add(b1, BorderLayout.CENTER);
bp.add(b2, BorderLayout.CENTER);      // b1'in yerine geçer
BorderLayout bl = (BorderLayout) bp.getLayout();
System.out.println(bp.getComponentCount());
System.out.println(((JButton) bl.getLayoutComponent(BorderLayout.CENTER)).getText());
2
Two

Bir bölgeye birden fazla bileşen koymanın yolu araya panel sokmak:

public EditorFrame() {
    super("Editor");
    add(new JLabel("Title bar text"), BorderLayout.NORTH);
    add(new JTextArea(10, 30), BorderLayout.CENTER);
    JPanel buttons = new JPanel();              // FlowLayout
    buttons.add(new JButton("Save"));
    buttons.add(new JButton("Cancel"));
    add(buttons, BorderLayout.SOUTH);           // iki buton tek bölgede
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    pack();                                     // tercih edilen boyuta göre küçült
}

pack() pencereyi bileşenlerin tercih ettiği boyuta göre ayarlar; setSize yerine kullanılabilir.

GridLayout

new GridLayout(satır, sütun) ya da boşluklu new GridLayout(satır, sütun, hgap, vgap). Bütün hücreler eşit boyutlu, bileşenler hücreyi tamamen doldurur. 300 x 100 piksellik bir panelde 2 x 3 ızgara:

JPanel gp = new JPanel(new GridLayout(2, 3));
for (int i = 1; i <= 6; i++) gp.add(new JButton("B" + i));
gp.setSize(300, 100);
gp.doLayout();
for (Component c : gp.getComponents()) {
    Rectangle r = c.getBounds();
    System.out.println(((JButton) c).getText() + " x=" + r.x + " y=" + r.y
                       + " w=" + r.width + " h=" + r.height);
}
B1 x=0 y=0 w=100 h=50
B2 x=100 y=0 w=100 h=50
B3 x=200 y=0 w=100 h=50
B4 x=0 y=50 w=100 h=50
B5 x=100 y=50 w=100 h=50
B6 x=200 y=50 w=100 h=50

TUZAK · önce satır. GridLayout(2, 3) 2 satır 3 sütun demek, tersi değil. Matrislerdeki gibi düşün: satır × sütun.

İç içe paneller

Gerçek ekranlar tek bir layout’la çıkmaz. Yöntem: ekranı bölgelere ayır, her bölgeye kendi layout’u olan bir JPanel koy. Örneğin bir hesap makinesi: pencere BorderLayout, NORTH’ta sonuç için JTextField, CENTER’da GridLayout(4, 3, 5, 5) ile tuş paneli.

setLayout(new GridLayout(4, 3, 5, 5));
String[] keys = {"1","2","3","4","5","6","7","8","9","*","0","#"};
for (String k : keys) add(new JButton(k));

Sık yapılan hatalar

Yanlış düşünceDoğrusu
int x = field.getText();String döner: Integer.parseInt(field.getText())
JTextField(10) en fazla 10 karakter alır10 sütunluk genişlik; sınır değil
JFrame’e üç buton add ettim, üçü görünürVarsayılan BorderLayout: hepsi CENTER’a, sadece son görünür
JPanel de BorderLayout ile gelirJPanel varsayılanı FlowLayout
GridLayout(2, 3) 2 sütun 3 satır2 satır, 3 sütun
Pencere kapandı, program bittiEXIT_ON_CLOSE yazmadıysan çalışmaya devam eder
setVisible(true) en başta olabilirBileşenleri ekledikten sonra, en son çağır
String s = pw.getPassword();char[] döner
GUI açmak için main gerekmezProgram yine main ile başlar, pencereyi orada yaratırsın

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

What is the default layout manager of (a) a JPanel and (b) the content pane of a JFrame?

Cevabı göster
(a) FlowLayout, (b) BorderLayout

new JPanel() FlowLayout ile gelir: bileşenler yan yana, satır dolunca alta. JFrame'in içerik paneli (content pane) BorderLayout ile gelir: add(c) konum belirtmezsen CENTER'a koyar. Bu ikisini karıştırmak, "neden sadece son butonum görünüyor" sorusunun kaynağıdır.

Soru 2orta

What is printed?

JPanel bp = new JPanel(new BorderLayout());
JButton b1 = new JButton("One"), b2 = new JButton("Two");
bp.add(b1, BorderLayout.CENTER);
bp.add(b2, BorderLayout.CENTER);
BorderLayout bl = (BorderLayout) bp.getLayout();
System.out.println(bp.getComponentCount());
System.out.println(((JButton) bl.getLayoutComponent(BorderLayout.CENTER)).getText());
Cevabı göster
2 ve Two

Panel iki bileşeni de içinde tutar (count 2). Ama BorderLayout'un her bölgesinde tek bileşen olur; aynı bölgeye ikinci ekleme birincinin yerini alır. Ekranda sadece "Two" görünür. Bir bölgeye birden fazla şey koymak istiyorsan önce onları bir JPanel'e koy, paneli bölgeye ekle.

Soru 3orta

A JPanel uses new GridLayout(2, 3) and buttons B1 to B6 are added in order. The panel is 300 x 100 pixels. Where is B4?

Cevabı göster
İkinci satırın ilk hücresi: x=0, y=50 (100 x 50)

GridLayout(satır, sütun): önce satır sayısı. Hücreler eşit boyutlu: 300 / 3 = 100 genişlik, 100 / 2 = 50 yükseklik. Doldurma soldan sağa, yukarıdan aşağı: B1 B2 B3 ilk satır, B4 ikinci satırın başı.

Soru 4kolay

Find the compile error.

JTextField f = new JTextField("7");
int x = f.getText();
Cevabı göster
getText() String döner, int'e atanamaz

javac: incompatible types: String cannot be converted to int. Metin kutusundan gelen her şey String'dir. Sayı için int x = Integer.parseInt(f.getText()); yaz.

Soru 5orta

What is printed?

JTextField num = new JTextField("12");
System.out.println(Integer.parseInt(num.getText()) * 2);
num.setText("12a");
try {
    Integer.parseInt(num.getText());
} catch (NumberFormatException e) {
    System.out.println("NumberFormatException: " + e.getMessage());
}
Cevabı göster
24 ve NumberFormatException: For input string: "12a"

"12" sayıya çevrilir, 24 basılır. "12a" sayı değil, parseInt NumberFormatException fırlatır; mesajı For input string: "12a". GUI'de kullanıcı her şeyi yazabilir, parse'ı try/catch içine al (9. hafta).

Soru 6kolay

JTextField field = new JTextField(10); What does 10 mean?

  1. The field holds at most 10 characters
  2. The initial text is "10"
  3. The preferred width is about 10 columns
  4. The field is 10 pixels wide
Cevabı göster
The preferred width is about 10 columns

int parametreli constructor sütun sayısını (columns) verir: kutunun tercih edilen genişliği yaklaşık 10 karakterlik. Uzunluk sınırı değildir; 26 karakterlik metin de yazılabilir (getText().length() 26 döner). Metin vermek için new JTextField("10").

Soru 7orta

Find the compile error.

JPasswordField p = new JPasswordField();
String s = p.getPassword();
Cevabı göster
getPassword() char[] döner

javac: incompatible types: char[] cannot be converted to String. Şifre güvenlik için String değil char dizisi olarak verilir (String bellekte değişmeden kalır, diziyi sıfırlayabilirsin). Gerekirse new String(p.getPassword()).

Soru 8zor

What is printed?

JButton jb = new JButton("OK");
System.out.println(jb instanceof JComponent);
System.out.println(jb instanceof Component);
Object lbl = new JLabel();
System.out.println(lbl instanceof AbstractButton);
System.out.println(new JCheckBox() instanceof AbstractButton);
Cevabı göster
true, true, false, true

Swing hiyerarşisi: Component ← Container ← JComponent ← AbstractButton ← JButton / JToggleButton ← JCheckBox. JButton hem JComponent hem Component'tir. JLabel AbstractButton'dan türemez: false. JCheckBox bir AbstractButton'dır. (Değişken JLabel tipinde olsaydı instanceof AbstractButton derlenmezdi: iki tip aynı çizgide değil.)

Soru 9orta

Find the problem: the window below appears empty and the program keeps running after it is closed.

public class App {
    public static void main(String[] args) {
        JFrame f = new JFrame("Demo");
        f.setVisible(true);
        f.add(new JLabel("Hello"));
        f.setSize(200, 100);
    }
}
Cevabı göster
setVisible en sona alınmalı; setDefaultCloseOperation(EXIT_ON_CLOSE) eksik

Bileşenleri ekle, boyutu ver, en son setVisible(true) çağır; görünür pencereye sonradan eklenen bileşen yeniden çizilene kadar görünmeyebilir. Varsayılan kapanış davranışı HIDE_ON_CLOSE: pencere gizlenir ama program bitmez. f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ekle. Derleme hatası yok, ikisi de mantık hatası.

Soru 10kolay

Which layout would you use for a calculator keypad with 4 rows of 3 equal buttons, and how do you create it with 5 pixel gaps?

Cevabı göster
new GridLayout(4, 3, 5, 5)

Eşit boyutlu hücreler gereken her ızgara GridLayout'tur. Parametreler: satır, sütun, yatay boşluk, dikey boşluk. FlowLayout pencere daralınca butonları kaydırır, BorderLayout'ta sadece 5 bölge var.

Soru 11orta

Write a class TemperatureFrame extending JFrame that shows, in a FlowLayout, a label "Celsius:", a 6-column text field, a "Convert" button and a result label "F: ?". The window title is "C to F" and closing it ends the program. (No event handling yet.)

Cevabı göster
JFrame alt sınıfı, constructor'da setLayout + add + setDefaultCloseOperation + setSize

Bileşenleri alan olarak tut ki 6. haftada listener içinden erişebilesin. JFrame varsayılanı BorderLayout olduğu için FlowLayout'u açıkça ayarla:

import java.awt.FlowLayout;
import javax.swing.*;

public class TemperatureFrame extends JFrame {
    private final JTextField celsiusField = new JTextField(6);
    private final JButton convertButton = new JButton("Convert");
    private final JLabel resultLabel = new JLabel("F: ?");

    public TemperatureFrame() {
        super("C to F");                 // pencere başlığı
        setLayout(new FlowLayout());
        add(new JLabel("Celsius:"));
        add(celsiusField);
        add(convertButton);
        add(resultLabel);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300, 100);
    }

    public static void main(String[] args) {
        new TemperatureFrame().setVisible(true);
    }
}

Doğrulama: Bileşen davranışları pencere açmadan headless modda çalıştırıldı (java -Djava.awt.headless=true verify/SEN1002/W05.java). JFrame varsayılanları (HIDE_ON_CLOSE, BorderLayout, CENTER ezme) pencere görünür yapılmadan kontrol edildi (java verify/SEN1002/W05b.java). Pencere örnekleri javac ile derlendi (verify/SEN1002/W05Gui.java). Derleme hataları: verify/SEN1002/err/E05a-c.java.

Deftere not kartı

import javax.swing.*; (bileşenler) import java.awt.*; (layout'lar, Color, Font) Pencere: class F extends JFrame { F() { super("Başlık"); ... } } Sıra: bileşenleri add → setSize/pack → setDefaultCloseOperation(EXIT_ON_CLOSE) → setVisible(true) EN SON. Varsayılan kapanış HIDE_ON_CLOSE: pencere gider, program sürer. JLabel: metin gösterir (setText/getText). JButton: tıklanır. JTextField(10) → 10 sütun genişlik, sınır DEĞİL. getText() → String → Integer.parseInt. JPasswordField.getPassword() → char[]. setEditable(false) → salt okunur. JPanel varsayılan FlowLayout: soldan sağa, satır dolunca alta, ortalı. JFrame içerik paneli varsayılan BorderLayout: NORTH SOUTH EAST WEST CENTER. add(c) konumsuz → CENTER. Bir bölgeye ikinci add birincinin YERİNE geçer. GridLayout(satır, sütun, hgap, vgap): eşit hücreler, soldan sağa yukarıdan aşağı. Karmaşık ekran: panelleri iç içe koy (her panelin kendi layout'u). Hiyerarşi: Component ← Container ← JComponent ← JLabel, AbstractButton ← JButton.