Bölüm 8: Grafikler
Giriş
Öğrenme Çıktıları
JFrame ve JPanel ile pencere ve panel oluşturmak, bileşen eklemek, pencereyi göstermek ve kapatmak.
paintComponent metodunu geçersiz kılıp Graphics ile şekil ve metin çizmek; ekran koordinatlarını kullanmak.
BorderLayout, FlowLayout ve GridLayout ile bileşenleri düzenlemek.
Dinleyici arayüzleri, adaptör sınıfları ve lambda ile fare ve düğme olaylarını işlemek.
Arayüzü SwingUtilities.invokeLater ile olay iş parçacığında kurmanın nedenini açıklamak.
Çizim programı, metin dönüştürücü ve para üstü hesaplayıcıyı okuyup değiştirebilmek.
Grafik Programlama
Grafik Programlama
- Java, grafik kullanıcı arayüzü (GUI, Graphical User Interface) geliştirmek için güçlü ve kapsamlı bir araç seti sunar: pencereler, düğmeler, metin alanları, çizimler.
- Böylece etkileşimli ve görsel uygulamalar yazılabilir. Bunun için üç kavramı anlamak gerekir:
Pencere, düğme, etiket, metin alanı gibi arayüzün yapı taşları. Her biri bir sınıfın nesnesidir.
Fare tıklaması, tuşa basma gibi kullanıcı eylemlerini yakalar ve bunlara yanıt veren metotları çağırır.
Şekilleri çizer, ekranı günceller ve bileşenlerin görüntülenmesini sağlar.
Benzetme: bir GUI programı, müşteriyi bekleyen bir dükkân gibidir. Vitrin (bileşenler) hazırlanır, sonra program bekler; müşteri ne zaman neye dokunursa (olay) ilgili görevli (dinleyici metot) çalışır.
Grafik Programlama
Konsol ve GUI Programı
| Konsol programı | GUI programı | |
|---|---|---|
| Akış | Sıralı: main baştan sona çalışır, soruyu program sorar | Olay güdümlü (event-driven): program kullanıcının hamlesini bekler |
| Girdi | Scanner ile klavyeden satır okunur | Düğme, metin alanı, fare; dinleyici metotlar çağrılır |
| Çıktı | System.out.println | Bileşenlerin metni değişir veya panel yeniden çizilir |
| Bitiş | main bitince program biter | main bitse de pencere kapanana kadar çalışır |
| Kütüphane | java.util, java.io | java.awt, javax.swing, java.awt.event |
Kural: GUI programında “sonra ne olacak?” sorusunun cevabı koddaki sıra değil, kullanıcının sıradaki eylemidir. Kodun büyük kısmı “şu olursa şunu yap” biçimindeki dinleyicilerden oluşur.
Grafik Programlama
AWT ve Swing
java.awtJava'nın ilk grafik kütüphanesi. Bugün en çok ortak sınıfları için kullanılır: Graphics, Color, Font, Point, yerleşim yöneticileri ve olay sınıfları (java.awt.event).
javax.swingAWT'nin üzerine kurulmuş, bileşenleri Java'nın kendisi çizen kütüphane. Sınıf adları J ile başlar: JFrame, JPanel, JButton, JLabel…
İçi boş üçgen: “türetilmiştir” (kalıtım). JFrame bir AWT penceresidir; diğer Swing bileşenleri JComponent'ten türer (JButton arada AbstractButton üzerinden).
Grafik Programlama
Uygulama Adımları
- Java'nın grafik sınıfları kullanılarak arayüze bileşenler eklenir.
- Kullanıcının eylemlerine yanıt veren olay işleme metotları tanımlanır.
paint()veyapaintComponent()gibi metotlarla görsel oluşturulur.
Ekran bir piksel ızgarasıdır. Koordinatlar tam sayıdır ve bileşenin sol üst köşesinden ölçülür. Matematikteki eksenin tersine, y aşağı doğru büyür: (0, 0) sol üst, (getWidth() − 1, getHeight() − 1) sağ alt pikseldir.
Grafik Programlama
Temel Grafik Kavramları
Üzerine resim çizilebilen yüzeydir. Swing'de genellikle JPanel (AWT'de Canvas) kullanılır: sınıfı JPanel'den türetir, çizimi onun içine yaparız.
Graphics sınıfıÇizim işlemlerini yapan “kalem”dir. Metotları: drawLine(), drawRect(), fillOval(), drawString(), drawImage()…
Color ve FontKalemin rengini Color, yazı tipini Font belirler: g.setColor(Color.RED), g.setFont(new Font("Serif", Font.BOLD, 24)).
Kalem benzetmesi: Graphics nesnesi elinizdeki kalemdir. setColor kalemi değiştirir; değiştirene kadar bütün çizimler aynı renkle yapılır.
Grafik Programlama
paintComponent ile Çizim
import java.awt.*; import javax.swing.*; class CizimPaneli extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); // zemini temizle g.setColor(Color.BLUE); g.drawRect(20, 30, 50, 70); // x, y, en, boy g.drawString("Merhaba, Java Grafik!", 100, 100); } }
paintComponent'i biz çağırmayız: pencere ilk açıldığında, boyutu değiştiğinde veyarepaint()istendiğinde Swing çağırır ve hazır birGraphics gverir.panel.getGraphics()ile de bir grafik nesnesi alınabilir, ama bununla yapılan çizim pencere bir sonraki kez yeniden çizildiğinde silinir. Kalıcı çizim için yolpaintComponent'tir.
Panel 300 × 160 iken sonuç. drawString'in y değeri yazının taban çizgisidir (harflerin altı).
Grafik Programlama
Graphics Çizim Metotları
| Metot | Ne yapar? |
|---|---|
drawLine(x1, y1, x2, y2) | İki nokta arasında çizgi |
drawRect(x, y, en, boy) | Dikdörtgen çerçevesi; (x, y) sol üst köşe |
fillRect(x, y, en, boy) | İçi dolu dikdörtgen |
drawOval(x, y, en, boy) | Bu dikdörtgene sığan elips (en = boy ise çember) |
fillOval(x, y, en, boy) | İçi dolu elips / daire |
drawString(s, x, y) | Metin; y taban çizgisi |
setColor(c) / setFont(f) | Sonraki çizimlerin rengi / yazı tipi |
drawImage(resim, x, y, null) | Bir resmi (x, y) noktasına çizer |
Elips ve daireler sınırlayıcı dikdörtgenleri ile verilir: merkez değil, sol üst köşe. Merkezi (cx, cy), yarıçapı r olan daire: fillOval(cx − r, cy − r, 2r, 2r).
Grafik Programlama
Şekiller: Kod ve Sonuç
class SekillerPaneli extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.drawLine(10, 10, 110, 60); g.drawRect(130, 10, 80, 50); g.fillRect(230, 10, 80, 50); g.drawOval(10, 80, 100, 60); g.fillOval(130, 80, 60, 60); g.drawString("Merhaba", 230, 115); } }
Renk verilmediğinde kalem, panelin ön plan rengi (Swing'de koyu gri) ile çizer. draw… çerçeve, fill… içi dolu şekil çizer.
Sırayla düşünün: çizimler üst üste boyanır; sonradan çizilen öncekini örter. Bu, birazdan göreceğimiz trafik işaretinin temel fikridir.
Pencere ve Panel
JFrame Sınıfı
Ekranda başlık çubuğu, simge durumuna küçültme ve kapatma düğmeleri olan üst düzey pencere (top-level window) oluşturur. Diğer bütün bileşenler bir pencerenin içinde yaşar.
| Kod | Görevi |
|---|---|
JFrame pencere = new JFrame("Başlık"); | Başlığı verilen pencereyi oluşturur (henüz görünmez) |
pencere.add(bilesen); | Pencereye (içerik bölmesine) bileşen ekler |
pencere.setSize(400, 300); | Genişlik ve yükseklik (piksel, kenarlıklar dahil) |
pencere.pack(); | Boyutu içerikteki bileşenlerin tercih ettiği boyuta göre ayarlar |
pencere.setLocation(x, y); | Pencerenin ekrandaki konumu; setLocationRelativeTo(null) ortalar |
pencere.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | Kapatılınca programı sonlandırır |
pencere.setVisible(true); | Pencereyi görünür yapar (genellikle en son çağrılır) |
Pencere ve Panel
İlk Pencere
import javax.swing.JFrame; public class JFrameOrnegi { public static void main(String[] args) { JFrame pencere = new JFrame("Java JFrame Örneği"); pencere.setSize(400, 300); pencere.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pencere.setVisible(true); } }
setVisible(true)unutulursa pencere oluşur ama görünmez.EXIT_ON_CLOSEverilmezse varsayılan davranışHIDE_ON_CLOSE'dur: pencere gizlenir ama program arka planda çalışmaya devam eder.- Boyut verilmezse pencere 0 × 0'a yakın, yalnız başlık çubuğu kadar açılır.
İçi boş, 400 × 300 boyutlu pencere (ölçekli).
Pencere ve Panel
JPanel Sınıfı
javax.swingpaketinde bulunan bir konteynerdir (container): içinde başka GUI öğeleri barındırır.- Bir pencerenin içindeki bileşenleri gruplamak ve düzenlemek için kullanılır; birden fazla panel iç içe konarak karmaşık düzenler kurulur.
setBackground(),setBorder(),setPreferredSize()ile renk, kenarlık ve boyut özelleştirilir.- Kendi çizimimizi, animasyon ve grafikleri yaptığımız tuvaldir:
JPanel'den türetippaintComponent'i geçersiz kılarız. - Kodu parçalara ayırmaya yarar: her panel ayrı bir sınıf olabilir.
Pencere ve Panel
JPanel Örneği
import java.awt.Color; import javax.swing.*; public class PanelOrnegi { public static void main(String[] args) { JPanel panel = new JPanel(); panel.setBackground(Color.LIGHT_GRAY); panel.add(new JButton("Tıkla")); JFrame pencere = new JFrame("JPanel Örneği"); pencere.add(panel); pencere.setSize(300, 150); pencere.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pencere.setVisible(true); } }
Color.LIGHT_GRAY = (192, 192, 192). Düğme panelin üst ortasında durur.
Neden üst ortada? JPanel'in varsayılan yerleşim yöneticisi FlowLayout'tur: bileşenleri tercih ettikleri boyutta, satır satır ve ortalı dizer. JFrame'in içerik bölmesi ise BorderLayout kullanır; bu yüzden panel bütün pencereyi kaplar.
Çizim Araçları · Kodu adım adım çalıştır
Point Sınıfı: (x, y) Noktası
Point nesnesi oluşur; x ve y alanları public int'tir.
getX() double döndürür: 30.0.
getY(): 40.0.
double değerler ondalıklı yazılır.
Alanlara doğrudan erişim int verir.
Orijine uzaklık: √(30² + 40²) = 50.0.
setLocation noktayı yeni konuma taşır. Değişkenler eski değerleri saklar.
toString() sınıf adını ve alanları yazar.
translate(dx, dy) göreli kaydırır. y azaldı: nokta yukarı çıktı.
Yeni konum yazılır.
static distance(x1, y1, x2, y2): (0, 0) ile (3, 4) arası 5.0.
Point nokta = new Point(30, 40); double xKoordinati = nokta.getX(); double yKoordinati = nokta.getY(); System.out.println(xKoordinati + " " + yKoordinati); System.out.println(nokta.x + " " + nokta.y); System.out.println(nokta.distance(0, 0)); nokta.setLocation(60, 80); System.out.println(nokta); nokta.translate(5, -10); // 5 sağa, 10 yukarı System.out.println(nokta); System.out.println(Point.distance(0, 0, 3, 4));
Çizim Araçları
Color Sınıfı
- Arayüzün ve çizimlerin renklendirilmesini sağlar (
java.awt.Color). - Hazır sabitler:
Color.RED,Color.GREEN,Color.WHITE,Color.LIGHT_GRAY… - RGB: kırmızı, yeşil, mavi bileşenleri 0–255 arası:
new Color(int red, int green, int blue). darker()bileşenleri 0,7 ile çarpar,brighter()0,7'ye böler (en çok 255): yeni bir renk nesnesi döner.
Color turuncu = new Color(255, 128, 0);
System.out.println(turuncu);
System.out.println(turuncu.getGreen());
System.out.println(Color.RED.darker());java.awt.Color[r=255,g=128,b=0] 128 java.awt.Color[r=178,g=0,b=0]
Color.RED (255, 0, 0)Color.BLUE (0, 0, 255)Color.LIGHT_GRAY (192, 192, 192)new Color(255, 128, 0) turuncuturuncu.darker() (178, 89, 0)Color.RED.darker() (178, 0, 0)Color.GREEN.darker() (0, 178, 0)new Color(100, 50, 0).brighter() (142, 71, 0)Çizim Araçları
Font Sınıfı
Metnin yazı tipini, stilini ve boyutunu belirler: new Font(ad, stil, punto).
| Parametre | Değerler |
|---|---|
| ad | "Serif", "SansSerif", "Monospaced", "Dialog" (her sistemde bulunan mantıksal adlar) veya "Arial" gibi gerçek bir yazı tipi |
| stil | Font.PLAIN (0), Font.BOLD (1), Font.ITALIC (2), ikisi birden: Font.BOLD | Font.ITALIC |
| punto | Tam sayı boyut, ör. 24 |
public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(new Color(0, 102, 204)); g.setFont(new Font("Serif", Font.BOLD, 24)); g.drawString("Java", 20, 40); g.setFont(new Font("Monospaced", Font.PLAIN, 14)); g.drawString("Grafik", 20, 70); }
JLabel gibi bileşenlerde de aynı nesne kullanılır: etiket.setFont(f). Mevcut bir yazı tipinden türetmek için: f.deriveFont(Font.ITALIC, 18f).
Yerleşim Yöneticileri
Yerleşim Yöneticisi
Yerleşim yöneticisi (layout manager), bir konteynerin içindeki bileşenlerin boyutunu ve konumunu kurallara göre hesaplayan nesnedir: panel.setLayout(new GridLayout(2, 3));. Koordinatları elle vermek yerine kullanılır, çünkü pencere büyüyünce, yazı tipi veya ekran çözünürlüğü değişince düzen kendiliğinden yeniden hesaplanır.
Soldan sağa, satır dolunca alta. JPanel varsayılanı.
Beş bölge. JFrame içerik bölmesi varsayılanı.
Eşit boyutlu hücrelerden ızgara: new GridLayout(2, 3).
Tek satır veya tek sütun: BoxLayout.Y_AXIS.
Daha esnek ama karmaşık olan GridBagLayout, farklı boyutlarda hücrelerden oluşan bir ızgara kurar. Panelleri iç içe koyup her birine basit bir yönetici vermek çoğu zaman yeterlidir.
Yerleşim Yöneticileri
BorderLayout Sınıfı
- Bileşenleri beş bölgeye yerleştirir:
NORTH,SOUTH,WEST,EAST,CENTER. - Önceliği farklı bileşenleri düzenli göstermeye yarar: araç çubuğu üstte, durum satırı altta, asıl içerik ortada.
- Bölge belirtilmeden eklenen bileşen merkeze yerleşir:
add(b)=add(b, BorderLayout.CENTER). - Her bölgede tek bileşen olur; aynı bölgeye ikinci ekleme öncekinin yerini alır.
- Kuzey ve güney tüm genişliği, batı ve doğu kalan yüksekliği alır; merkez geriye kalan her şeyi doldurur.
setHgap()vesetVgap()bölgeler arasındaki yatay ve dikey boşluğu (piksel) ayarlar.
Yerleşim Yöneticileri · Kodu adım adım çalıştır
BorderLayout: Bölgeleri Doldurma
Panel BorderLayout kullanacak; bölgeler arasında 5 piksel boşluk var. Henüz bileşen yok.
Kuzey düğmesi tüm genişliği alır, yüksekliği düğmenin tercih ettiği kadardır.
Güney de tüm genişlikte, en alta yerleşir.
Batı, kuzey ile güney arasında kalan yüksekliği alır; genişliği metne göre.
Doğu sağ kenara yerleşir.
Merkez geriye kalan bütün alanı doldurur. Pencere büyütülürse en çok merkez büyür.
JPanel panel = new JPanel(); BorderLayout yerlesim = new BorderLayout(); yerlesim.setHgap(5); yerlesim.setVgap(5); panel.setLayout(yerlesim); panel.add(new JButton("Kuzey"), BorderLayout.NORTH); panel.add(new JButton("Güney"), BorderLayout.SOUTH); panel.add(new JButton("Batı"), BorderLayout.WEST); panel.add(new JButton("Doğu"), BorderLayout.EAST); panel.add(new JButton("Merkez"), BorderLayout.CENTER);
Giriş Yasak İşareti
Giriş Yasak İşareti
Hedef: trafikteki “girişi olmayan yol” işaretini çizen bir panel. Şekil üst üste boyanan dört katmandan oluşur:
- Yeşil arka plan (
setBackground) - Beyaz büyük daire (dış halka)
- Kırmızı, biraz küçük daire
- Ortada beyaz yatay bar
Bütün ölçüler getWidth() ve getHeight()'tan hesaplanır: pencere büyütülünce işaret de büyür ve hep ortada kalır.
Giriş Yasak İşareti
paintComponent Kodu
public class GirisYasaktirIsareti extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); var c = new Point(getWidth() / 2, getHeight() / 2); // merkez var r = Math.min(getWidth() / 2, getHeight() / 2) - 5; // yarıçap var r2 = (int) (r * 0.9); // iç yarıçap var bw = (int) (r2 * 1.4); // bar genişliği var bh = (int) (r2 * 0.35); // bar yüksekliği g.setColor(Color.WHITE); g.fillOval(c.x - r, c.y - r, 2 * r, 2 * r); g.setColor(Color.RED); g.fillOval(c.x - r2, c.y - r2, r2 * 2, r2 * 2); g.setColor(Color.WHITE); g.fillRect(c.x - bw / 2, c.y - bh / 2, bw, bh); } }
var (Java 10+): türü derleyici sağdaki ifadeden çıkarır; burada c bir Point, diğerleri int. Klasik yazım: Point c = new Point(…); int r = …;. (int) dönüşümü ondalık kısmı atar. Gerekli içe aktarmalar: java.awt.* ve javax.swing.*.
Giriş Yasak İşareti · Kodu adım adım çalıştır
İşareti Çizme: paintComponent İzleme
Panel 384 × 261 piksel. super.paintComponent zemini arka plan rengiyle (yeşil) boyar.
Merkez: (384 / 2, 261 / 2) = (192, 130). Tam sayı bölmesi: 130,5 → 130.
Yarıçap: min(192, 130) − 5 = 125. Kısa kenara göre seçilir, 5 piksel pay kalır.
İç yarıçap: (int) 112,5 = 112.
Bar genişliği: (int) (112 × 1,4) = (int) 156,8 = 156.
Bar yüksekliği: (int) 39,2 = 39.
fillOval(67, 5, 250, 250): beyaz daire, sınırlayıcı kare kesikli çizgide.
fillOval(80, 18, 224, 224): kırmızı daire beyazın üstüne boyanır; 13 piksellik beyaz halka kalır.
fillRect(114, 111, 156, 39): beyaz bar en üste. İşaret tamam.
super.paintComponent(g); var c = new Point(getWidth() / 2, getHeight() / 2); var r = Math.min(getWidth() / 2, getHeight() / 2) - 5; var r2 = (int) (r * 0.9); var bw = (int) (r2 * 1.4); var bh = (int) (r2 * 0.35); g.setColor(Color.WHITE); g.fillOval(c.x - r, c.y - r, 2 * r, 2 * r); g.setColor(Color.RED); g.fillOval(c.x - r2, c.y - r2, r2 * 2, r2 * 2); g.setColor(Color.WHITE); g.fillRect(c.x - bw / 2, c.y - bh / 2, bw, bh);
Giriş Yasak İşareti
main ve Pencere
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { var panel = new GirisYasaktirIsareti(); panel.setBackground(Color.GREEN.darker()); var frame = new JFrame("Basit bir grafik programı"); frame.setSize(400, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.setVisible(true); }); }
SwingUtilities.invokeLater(() -> { … }): arayüzü olay iş parçacığında kurar (birazdan).() -> { … }bir lambda ifadesidir (Java 8+): “sonra çalıştırılacak kod parçası”.Color.GREEN.darker()= (0, 178, 0): saf yeşilden biraz koyu.- Panel, pencerenin içerik bölmesinin merkezine eklenir; tek bileşen olduğu için tüm alanı kaplar.
- 400 × 300 pencerede kenarlık ve başlık çubuğu düşülünce panel yaklaşık 384 × 261 olur (işletim sistemine göre değişir).
Fare Olayları
Etkileşimli Bilgisayar Grafikleri
- Bilgisayar grafikleri, görsel içerik oluşturma ve işleme sürecidir. Etkileşimli (interactive) grafiklerde kullanıcı fare, klavye gibi giriş aygıtlarıyla görüntüyü değiştirir: çizim programları, oyunlar, haritalar.
- Grafik kütüphaneleri şekil çizmeyi, renklendirmeyi ve grafik nesnelerini değiştirmeyi sağlar; etkileşim ise aşağıdaki olay döngüsü ile gerçekleşir.
Dinleyici metot ekranı doğrudan boyamaz: veriyi günceller ve repaint() ile yeniden çizim ister. Swing uygun anda paintComponent'i çağırır.
Fare Olayları
Dinleyici Arayüzü ve Adaptör
Arayüz: beş metodun hepsi yazılmalı
class Dinleyici implements MouseListener { public void mouseClicked(MouseEvent e) { System.out.println("tık"); } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } }
MouseAdapter: yalnız gerekeni geçersiz kıl
class Dinleyici extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { System.out.println("tık"); } } // panel.addMouseListener(new Dinleyici());
MouseAdapter,MouseListener(veMouseMotionListener,MouseWheelListener) arayüzünü boş gövdelerle uygulayan hazır bir sınıftır. Ondan türeyen sınıf yalnızca ilgilendiği metodu yazar.- Hareket olayları ayrı bir arayüzdedir:
MouseMotionListener(mouseDragged,mouseMoved); adaptörüMouseMotionAdapter. Kayıt:addMouseListener(…),addMouseMotionListener(…). new MouseAdapter() { … }biçimi bir anonim iç sınıftır (anonymous inner class): adı olmayan, tek kullanımlık bir alt sınıf.
Fare Olayları
Fare Olay Metotları
| Metot | Arayüz | Ne zaman çağrılır? |
|---|---|---|
mousePressed(e) | MouseListener | Fare düğmesine basıldığı an |
mouseReleased(e) | MouseListener | Düğme bırakıldığında |
mouseClicked(e) | MouseListener | Basıp aynı yerde bırakınca (pressed → released → clicked) |
mouseEntered(e) / mouseExited(e) | MouseListener | İşaretçi bileşenin içine girince / dışına çıkınca |
mouseMoved(e) | MouseMotionListener | Düğmeye basmadan hareket edince |
mouseDragged(e) | MouseMotionListener | Düğme basılıyken hareket edince (sürükleme) |
mouseWheelMoved(e) | MouseWheelListener | Tekerlek döndürülünce |
Her metot bir MouseEvent e parametresi alır: olayın nerede, hangi düğmeyle, hangi tuşlar basılıyken olduğu bu nesnededir.
Fare Olayları
MouseEvent Sınıfı
| Metot | Döndürdüğü |
|---|---|
getX(), getY() | İşaretçinin bileşene göre konumu |
getPoint() | Aynı konum, Point olarak |
getButton() | BUTTON1 sol, BUTTON2 orta, BUTTON3 sağ |
getClickCount() | Art arda tıklama sayısı (2: çift tık) |
isShiftDown(), isCtrlDown(), isAltDown() | O anda tuş basılı mı? |
panel.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { String dugme = "sol"; if (e.getButton() == MouseEvent.BUTTON3) { dugme = "sağ"; } System.out.println(dugme + " (" + e.getX() + ", " + e.getY() + ")" + (e.isShiftDown() ? " + Shift" : "")); } });
Fare olayı sonrasında oluşan durumu temsil eder. Koordinatlar ekrana değil bileşene göredir: panelin sol üst köşesi (0, 0).
Fare Olayları
Deneyin: Fare Olayları
Java çağrısı ve MouseEvent değerleri
(henüz olay yok)
Tarayıcı olayları Java'daki karşılıklarıyla gösterilir. Tıklamanın pressed → released → clicked sırasıyla üç olay ürettiğine, sürüklerken çok sayıda mouseDragged geldiğine dikkat edin. Sağ tık BUTTON3'tür.
Fare Olayları
SwingUtilities ve Olay İş Parçacığı
javax.swing.SwingUtilitiesyardımcı metotlar içerir.- Swing bileşenleri iş parçacığı güvenli değildir (not thread-safe): aynı anda birden çok iş parçacığı dokunursa tutarsızlık olabilir.
- Kural: bileşenler yalnızca olay iş parçacığından (Event Dispatch Thread, EDT) kurulur ve değiştirilir. Dinleyici metotlar zaten EDT'de çalışır.
- Benzetme: mutfakta tek bir aşçı (EDT) vardır; diğerleri yemeği kendileri yapmaz, sipariş fişi (
Runnable) bırakır.
| Metot | Davranış |
|---|---|
invokeLater(iş) | İşi kuyruğa koyar ve hemen döner (asenkron). GUI başlatmanın standart yolu. |
invokeAndWait(iş) | İş EDT'de bitene kadar bekler (senkron). EDT'nin içinden çağrılırsa Error fırlatır (EDT kendini bekleyemez). |
Çizim Programı
Basit Çizim Programı
egriler.size() = 0, toplam nokta = 0Fareyle basılı tutup sürükleyerek çizim yapılan program. Panelin içinde deneyin.
- Basınca (
mousePressed) yeni bir eğri başlar. - Sürükledikçe (
mouseDragged) eğrinin sonuna nokta eklenir verepaintistenir. - Çizerken (
paintComponent) her eğrinin ardışık noktaları çizgilerle birleştirilir.
Program çizimi bir resim olarak değil, noktaların listesi olarak saklar; ekran her seferinde bu veriden yeniden çizilir.
Çizim Programı
Eğrileri Saklamak
public class BasitCizimPaneli extends JPanel { private List<List<Point>> egriler = new ArrayList<>(); public BasitCizimPaneli() { addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { var yeniEgri = new ArrayList<Point>(); yeniEgri.add(new Point(e.getX(), e.getY())); egriler.add(yeniEgri); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { egriler.get(egriler.size() - 1) .add(new Point(e.getX(), e.getY())); repaint(0, 0, getWidth(), getHeight()); } }); } }
List<List<Point>>: listelerin listesi. Dış liste eğrileri, her iç liste bir eğrinin noktalarını tutar.- Son eğri:
egriler.get(egriler.size() - 1). Son eleman indeksisize() - 1'dir;get(size())IndexOutOfBoundsExceptionfırlatır.
Çizim Programı · Kodu adım adım çalıştır
Fare Olaylarını İzleme
mousePressed: Basıldı (40, 120): yeni bir iç liste oluşur, ilk noktası eklenir, dış listeye konur.
mouseDragged: Sürüklendi (60, 60): son eğriye nokta eklenir; repaint yeniden çizimi ister, çizgi görünür.
mouseDragged: Sürüklendi (80, 120): aynı eğri uzar. Gerçekte saniyede onlarca mouseDragged gelir.
mouseReleased: Bırakıldı: mouseReleased'ı yazmadık; MouseAdapter'ın boş gövdesi çalışır, hiçbir şey olmaz.
mousePressed: Yeniden basıldı (50, 95): ikinci eğri başlar. Eski eğri olduğu gibi kalır.
mouseDragged: Sürüklendi (70, 95): ekleme yine son eğriye, yani eğri 1'e yapılır.
addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { var yeniEgri = new ArrayList<Point>(); yeniEgri.add(new Point(e.getX(), e.getY())); egriler.add(yeniEgri); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { egriler.get(egriler.size() - 1) .add(new Point(e.getX(), e.getY())); repaint(0, 0, getWidth(), getHeight()); } });
Çizim Programı · Kodu adım adım çalıştır
Eğrileri Çizmek: paintComponent
Zemin temizlenir; önceki çizimden iz kalmaz.
İlk eğri; onceki onun ilk noktası.
İç döngü ilk noktadan başlar: kendisiyle birleşir, tek piksel çizilir. Ardından onceki = nokta.
(40,120) → (60,60) çizgisi.
(60,60) → (80,120). Eğri 0 bitti.
Dış döngü ikinci eğriye geçer; onceki yeniden onun ilk noktası olur. Böylece iki eğri birleşmez.
Yine tek piksel.
(50,95) → (70,95). Beş drawLine çağrısıyla çizim tamam.
@Override public void paintComponent(Graphics g) { super.paintComponent(g); for (var egri : egriler) { var onceki = egri.get(0); for (var nokta : egri) { g.drawLine(onceki.x, onceki.y, nokta.x, nokta.y); onceki = nokta; } } }
Çizim Programı
main ve repaint
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { var frame = new JFrame("Basit Çizim Programı"); frame.getContentPane().add(new BasitCizimPaneli(), BorderLayout.CENTER); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300); frame.setVisible(true); }); }
repaint()çizimi hemen yapmaz: kuyruğa bir “yeniden boya” isteği koyar. Art arda gelen istekler birleştirilebilir.repaint(x, y, en, boy)yalnızca o dikdörtgenin yeniden çizilmesini ister. Swing bunun dışını kırpar (clipping):go bölgenin dışına boyamaz, iş azalır.repaint(0, 0, getWidth(), getHeight())=repaint().super.paintComponent(g)unutulursa zemin temizlenmez; eski görüntü artıkları kalabilir.paintComponent'i kendimiz çağırmayız; veriyi değiştiriprepaint()isteriz.
Metin Bileşenleri
JButton Sınıfı
- Tıklanabilir düğmeler oluşturur; metin, ikon ve olay işleyicilerle özelleştirilir.
setText()metni,setIcon()ikonu belirler;setEnabled(false)pasif yapar.addActionListener()tıklanınca çalışacak dinleyiciyi ekler (ActionListener, tek metoduactionPerformed).
class SayacPaneli extends JPanel { private int sayac = 0; private JLabel etiket = new JLabel("Henüz tıklanmadı"); SayacPaneli() { JButton dugme = new JButton("Tıkla"); dugme.addActionListener(e -> { sayac++; etiket.setText(sayac + " kez tıklandı"); }); add(dugme); add(etiket); } }
Metin Bileşenleri
ActionListener: Anonim Sınıf ve Lambda
Anonim iç sınıf (klasik)
dugme.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { sayac++; etiket.setText("Sayı: " + sayac); } });
Lambda ifadesi (Java 8+)
dugme.addActionListener(e -> {
sayac++;
etiket.setText("Sayı: " + sayac);
});
// tek ifade ise süslü parantez gerekmez:
dugme.addActionListener(e -> System.exit(0));ActionListeneryalnızca tek soyut metodu olan bir arayüzdür (fonksiyonel arayüz). Bu yüzden derleyicie -> …lambdasınıactionPerformed'ın gövdesi olarak kabul eder.MouseListener'ın beş metodu olduğu için lambda ile yazılamaz; orada adaptör sınıfı kullanılır.- Lambda içinden yerel değişken değiştirilemez:
int sayac = 0; … e -> sayac++derlenmez. Sayaç gibi değişen değerler alan (field) olmalıdır.
Metin Bileşenleri
JLabel ve JTextField
JLabel: etiketMetin veya resim gösterir; kullanıcı değiştiremez.
setText() metni, setIcon() resmi, setFont() yazı tipini, setForeground() metin rengini belirler.
JTextField: tek satırlık metin alanıKullanıcının metin girmesi için; formlardan veri toplanır.
getText() / setText() okur ve yazar, setColumns() görünen sütun sayısını belirler, addActionListener() Enter'a basılınca çalışır.
JLabel etiket = new JLabel("Adınız:"); etiket.setFont(new Font("SansSerif", Font.BOLD, 14)); etiket.setForeground(Color.BLUE); JTextField adAlani = new JTextField(12); JLabel selam = new JLabel(" "); adAlani.addActionListener(e -> selam.setText( "Merhaba, " + adAlani.getText() + "!"));
Metin Bileşenleri
JTextArea ve JScrollPane
JTextArea: çok satırlı metinİçerik okunabilir, atanabilir, güncellenebilir. new JTextArea(satır, sütun) ile görünür boyut önceden verilir. getText(), setText(), append() (sona ekle), setEditable(false) (salt okunur), setLineWrap(true) (satır kaydır).
JScrollPane: kaydırma bölmesiİçine konan bileşen (metin alanı, tablo, resim) ekrana sığmadığında otomatik kaydırma çubukları ekler; içerik boyutu değişince kendini uyarlar. setPreferredSize() ile boyutu, setViewportView() ile içeriği değiştirilir.
JTextArea alan = new JTextArea(5, 20); alan.setText("Birinci satır\n"); alan.append("İkinci satır\n"); JScrollPane kaydirma = new JScrollPane(alan); System.out.print(alan.getText()); System.out.println(alan.getLineCount());
Birinci satır İkinci satır 3
Harf Çevirici
Metin Dönüştürücü: Pencere
- Ortada (
CENTER) birJScrollPaneiçinde 8 satır × 50 sütunlukJTextArea. - Altta (
SOUTH) iki düğmeyi taşıyan birJPanel(FlowLayout: ortalı). pack()pencere boyutunu bu bileşenlerin tercih ettiği boyuttan hesaplar.- Düğmeler metni
toLowerCase()/toUpperCase()ile dönüştürüp alana geri yazar.
Not: toUpperCase() bilgisayarın yerel ayarını (locale) kullanır. İngilizce ayarlı bir makinede "istanbul" → "ISTANBUL", Türkçe ayarlıda "İSTANBUL" olur. Sonucu sabitlemek için: toUpperCase(Locale.forLanguageTag("tr")). Bu demo Türkçe kuralları kullanır.
Harf Çevirici
Metin Dönüştürücü: Kod
import java.awt.BorderLayout; import javax.swing.*; public class BuyukHarfeCevirici { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { var alan = new JTextArea("Java Programlama", 8, 50); var kucukHarfButton = new JButton("Küçük Harfe Çevir"); var buyukHarfButton = new JButton("Büyük Harfe Çevir"); kucukHarfButton.addActionListener(e -> alan.setText(alan.getText().toLowerCase())); buyukHarfButton.addActionListener(e -> alan.setText(alan.getText().toUpperCase())); var panel = new JPanel(); panel.add(kucukHarfButton); panel.add(buyukHarfButton); var frame = new JFrame("BuyukHarfeCevirici"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new JScrollPane(alan), BorderLayout.CENTER); frame.getContentPane().add(panel, BorderLayout.SOUTH); frame.pack(); frame.setVisible(true); }); } }
- Tüm arayüz tek bir lambda içinde, EDT'de kurulur.
alanyerel bir değişkendir ama lambda içinde yalnızca okunduğu (değiştirilmediği) için kullanılabilir (effectively final).toUpperCase()yeni birStringdöndürür;Stringdeğişmez olduğu için sonucusetTextile geri yazmak gerekir.BorderLayout.CENTERtam adıyla yazılır; yalnızCENTERderlenmez.
Harf Çevirici
Bir Tıklamada Neler Olur?
Kullanıcı “Büyük Harfe Çevir” düğmesine tıklar; işletim sistemi bunu Java'ya iletir.
Java bir olay nesnesi oluşturup olay kuyruğuna koyar.
EDT sıradaki olayı alır; düğme, kayıtlı tüm ActionListener'ların actionPerformed'ını çağırır: bizim lambdamız çalışır.
alan.getText() mevcut metni okur.
toUpperCase() yeni bir dizgi üretir; eski dizgi değişmez.
setText alanın modelini günceller; alan kendini repaint ile yeniden çizdirir. EDT sıradaki olaya geçer.
Para Üstü Uygulaması
Para Üstü Hesaplama
Girilen kuruş miktarının en az sayıda madeni parayla (50, 25, 10, 5, 1) nasıl ödeneceğini gösterir.
- Düğme yoktur: rapor her tuş vuruşunda kendiliğinden güncellenir.
- Bunu metin alanının belgesine (
Document) eklenen birDocumentListenersağlar. - Deneyin: 93, 287, boş alan,
abc,-7.
Para Üstü Uygulaması
Alanlar ve Yapıcı
public class ParaUstuHesaplama extends JFrame { private JTextField miktarAlani = new JTextField(12); private Document metin = miktarAlani.getDocument(); private JTextArea rapor = new JTextArea(8, 40); public ParaUstuHesaplama() { var ustPanel = new JPanel(); ustPanel.add(new JLabel("Miktar:")); ustPanel.add(miktarAlani); getContentPane().add(ustPanel, BorderLayout.NORTH); getContentPane().add(new JScrollPane(rapor), BorderLayout.CENTER); setBackground(Color.LIGHT_GRAY); rapor.setEditable(false); metin.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { raporuGuncelle(); } public void removeUpdate(DocumentEvent e) { raporuGuncelle(); } public void changedUpdate(DocumentEvent e) { raporuGuncelle(); } }); }
- Sınıf
JFrame'den türer. Swing'de veri ile görünüm ayrıdır (MVC):JTextFieldgörünüm,getDocument()ile alınanDocumentmetni tutan model. DocumentListener:insertUpdate(eklendi),removeUpdate(silindi),changedUpdate(stil değişti; düz metinde çağrılmaz). Arayüz olduğu için üçü de yazılır.
Para Üstü Uygulaması · Kodu adım adım çalıştır
raporuGuncelle: 93 kuruş
Alandaki metin "93" okunur ve tam sayıya çevrilir.
Para değerleri büyükten küçüğe bir dizide.
setText raporu sıfırdan başlatır: eski içerik silinir.
x = 50: 93 / 50 = 1 adet (tam sayı bölmesi), satır sona eklenir.
Kalan: 93 % 50 = 43.
x = 25: 43 / 25 = 1 adet (tam sayı bölmesi), satır sona eklenir.
Kalan: 43 % 25 = 18.
x = 10: 18 / 10 = 1 adet (tam sayı bölmesi), satır sona eklenir.
Kalan: 18 % 10 = 8.
x = 5: 8 / 5 = 1 adet (tam sayı bölmesi), satır sona eklenir.
Kalan: 8 % 5 = 3.
x = 1: 3 / 1 = 3 adet (tam sayı bölmesi), satır sona eklenir.
Kalan: 3 % 1 = 0.
void raporuGuncelle() { try { var miktar = Integer.parseInt( metin.getText(0, metin.getLength())); int[] dizi = {50, 25, 10, 5, 1}; rapor.setText(miktar + " kuruş yapmak için kullanılacaklar:\n"); for (int x : dizi) { rapor.append(miktar / x + " " + x + " kuruş\n"); miktar %= x; } } catch (NumberFormatException e) { rapor.setText("Tam sayı değil veya aralık dışında"); } catch (Exception e) { rapor.setText(e.toString()); } }
Para Üstü Uygulaması
main ve Olayların Sırası
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { var frame = new ParaUstuHesaplama(); frame.setTitle("Para Üstü Hesaplama"); frame.pack(); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); }
| Kullanıcı / kod | Olay → rapor |
|---|---|
9 yazar | insertUpdate → 9 kuruş: 1 × 5, 4 × 1 |
3 yazar | insertUpdate → 93 kuruş raporu |
| Hepsini siler | removeUpdate → parseInt(""): “Tam sayı değil…” |
abc veya 99999999999 | NumberFormatException → aynı mesaj |
setText("287") | iki olay: önce removeUpdate, sonra insertUpdate → 5, 1, 1, 0, 2 |
-7 | Geçerli sayı! 0, 0, 0, −1, −2: kontrol eksik |
İlk catch beklenen hatayı (sayı olmayan metin, int sınırını aşan değer) yakalar. İkinci catch diğer her şey için genel güvencedir; getText(0, n) işaretli (checked) BadLocationException fırlatabildiği için gereklidir.
Özet
İyi Uygulamalar
Koordinatları elle vermeyin; farklı ekran boyutu ve çözünürlükte düzen bozulmaz.
b1, t2 yerine buyukHarfButton, miktarAlani: onlarca bileşenli kod okunur kalır.
“Kullanıcı şunu yapınca ne olmalı?” sorusuyla tasarlayın; her eylem için bir dinleyici.
Para üstü hesabı gibi kuralları ayrı bir metotta veya sınıfta tutun; arayüzsüz test edilebilir, başka arayüzde de kullanılır.
main'in içi: SwingUtilities.invokeLater(() -> { … }).
Dinleyicide uzun bir iş (dosya indirme, büyük hesap) yapılırsa pencere donar; bu işler ayrı iş parçacığına taşınır (Bölüm 9).
Özet
Özet
| Kavram | Özü |
|---|---|
| Paketler | java.awt: Graphics, Color, Font, Point, yerleşim, olaylar · javax.swing: J… bileşenleri |
JFrame | Pencere: setSize/pack, setDefaultCloseOperation(EXIT_ON_CLOSE), en son setVisible(true) |
JPanel | Konteyner ve tuval; varsayılan FlowLayout |
| Çizim | paintComponent(Graphics g) geçersiz kılınır, önce super.paintComponent(g); değişince repaint() |
| Koordinat | (0, 0) sol üst; x sağa, y aşağı; ovaller sınırlayıcı dikdörtgenle verilir |
| Yerleşim | BorderLayout (5 bölge, içerik bölmesi varsayılanı), FlowLayout, GridLayout, BoxLayout |
| Olaylar | Dinleyici arayüzü + add…Listener; çok metotlu arayüzler için adaptör; tek metotlu için lambda |
| Fare | MouseListener, MouseMotionListener; MouseEvent: getX, getY, getButton |
| EDT | Swing tek iş parçacıklıdır; invokeLater asenkron, invokeAndWait senkron |
| Metin | JLabel gösterir, JTextField tek satır, JTextArea çok satır, JScrollPane kaydırır |
Özet
Sık Yapılan Hatalar
JFrame için import javax.swing.*; yoksa: cannot find symbol … class JFrame.
Pencere hiç görünmez veya kapatılınca program arka planda çalışmaya devam eder.
paintcomponent yeni bir metottur, hiç çağrılmaz. @Override ile: method does not override or implement a method from a supertype.
Zemin temizlenmez; eski çizim artıkları ekranda kalır.
Dinleyici is not abstract and does not override abstract method mouseExited(MouseEvent) in MouseListener. Çözüm: MouseAdapter.
IndexOutOfBoundsException: Index 1 out of bounds for length 1. Son eleman size() - 1.
e -> sayac++: local variables referenced from a lambda expression must be final or effectively final. Alan kullanın.
add(bilesen, CENTER): cannot find symbol … variable CENTER. BorderLayout.CENTER yazın.
Özet
Örnek Problemler
- Giriş yasak işareti paneli 200 × 100 piksel olsaydı
r,r2,bw,bhkaç olurdu? - Para üstü programına
68yazılınca rapor ne olur? g.drawOval(50, 50, 100, 60)ile çizilen elipsin merkezi neresidir?BorderLayoutkullanan bir panele önceA, sonraBdüğmesi aynıNORTHbölgesine eklenirse kuzeyde hangisi görünür?- Kullanıcı panele bir kez tıklayıp bırakırsa
MouseListener'ın hangi metotları hangi sırayla çağrılır? Color.RED.brighter()hangi rengi verir?
Cevapları göster
r= min(100, 50) − 5 = 45,r2= (int) 40,5 = 40,bw= 56,bh= 14.- “68 kuruş yapmak için kullanılacaklar:” ardından 1 50, 0 25, 1 10, 1 5, 3 1 kuruş.
- (50 + 100/2, 50 + 60/2) = (100, 80).
- B: her bölgede tek bileşen olur, son eklenen kazanır (A panelde kalır ama yerleşimde yer almaz).
mousePressed→mouseReleased→mouseClicked(işaretçi panele girerken ayrıcamouseEntered).- Yine (255, 0, 0): kırmızı zaten en parlak; 0 olan yeşil ve mavi bileşenler 0 kalır.
Özet
Alıştırmalar
- Hedef tahtası: panel boyutuna göre ölçeklenen, kırmızı-beyaz 5 iç içe halka çizin.
- Sayaç penceresi: “−1”, “+1”, “Sıfırla” düğmeleri ve değeri gösteren bir
JLabel. - Çizim programına bir “Temizle” düğmesi ekleyin.
- Çizim programında sağ tık son eğriyi silsin.
- Para üstü programı negatif miktarlar için “Miktar pozitif olmalı” yazsın.
- Sıcaklık dönüştürücü:
JTextField'a °C girilip düğmeye basılınca °F gösterilsin.
Cevaplar / ipuçları
- Döngü:
int ri = r - i * r / 5;g.setColor(i % 2 == 0 ? Color.RED : Color.WHITE);g.fillOval(cx - ri, cy - ri, 2 * ri, 2 * ri);Büyükten küçüğe çizin. private static int sayac;alanı;arti.addActionListener(e -> etiket.setText("" + (++sayac)));, panelFlowLayout.- Düğme dinleyicisi:
egriler.clear(); repaint();(panel ile düğmeBorderLayout'ta CENTER ve SOUTH). mousePressedbaşında:if (e.getButton() == MouseEvent.BUTTON3) { if (!egriler.isEmpty()) egriler.remove(egriler.size() - 1); repaint(); return; };mouseDraggedbaşınaif (!SwingUtilities.isLeftMouseButton(e)) return;parseInt'ten sonra:if (miktar < 0) { rapor.setText("Miktar pozitif olmalı"); return; }double c = Double.parseDouble(alan.getText()); sonuc.setText(c * 9 / 5 + 32 + " °F");100 → 212.0 °F; hatalı girdi içinNumberFormatExceptionyakalayın.
Özet
Kaynaklar
- Oracle, The Java Tutorials: Creating a GUI With Swing; Performing Custom Painting; Laying Out Components Within a Container; Writing Event Listeners; Concurrency in Swing.
- Oracle, Java SE 21 API Documentation:
java.awt.Graphics,java.awt.Color,javax.swing.JFrame,javax.swing.SwingUtilities. - P. Deitel ve H. Deitel, Java: How to Program, 11. baskı, Pearson (Bölüm 12: GUI Components; Bölüm 13: Graphics and Java 2D).
- K. Sierra ve B. Bates, Head First Java, 3. baskı, O'Reilly (Bölüm 14–15: GUI, Swing ve olaylar).
- C. S. Horstmann, Core Java, Volume I: Fundamentals, 12. baskı, Pearson (Bölüm 10–11: grafik programlama, olay işleme, Swing).
- A. B. Downey ve C. Mayfield, Think Java, 2. baskı, O'Reilly (Ek: Java 2D Graphics).