Hakkında Künye

Swing ile GUI Programlama III

Merhabalar herkese,

Bu ay, Swing rehberimizin son bölümünde, programlarımıza menü çubuğu eklemeyi ve klavye ile yaratılan olayları nasıl yöneteceğimizi öğreneceğiz. Bu sayede daha düzenli arayüzler oluşturup, kullanıcı klavyeden veri girdiği anda kullanıcıya cevap verebileceğiz.

Menü Çubuğu Nedir?

Menü çubuğu, kullanıcının bir komut seçmesini sağlayan ve genellikle ekranın en üstünde bulunan bir elemandır. Birbiriyle ilişkili komutlar aynı başlık altında toplanır. Bu sayede hem ekranda daha az yer kaplanır hem de kullanıcının aradığı bir komutu zorlanmadan bulması sağlanır. Hemen hemen tüm kullanıcı arayüzlerinde menü çubuğu bulunur. Öyleyse bizim programlarımızda neden olmasın diye düşünüyorsanız, bu ayki ilk örneğimizde Swing programlarımıza nasıl menü çubuğu ekleyebileceğimizi inceleyelim.





Programımızda menü çubuğumuz için kullanacağımız bileşenler JMenuBar, Jmenu, JMenuItem, JcheckBoxMenuItem ve JRadioButtonMenuItem'dır. Şimdi bunların ne işe yaradığını sizlere kısaca açıklayayım. JmenuBar, ekrandaki menü çubuğunu temsil eden sınıftır. JMenu, menü   çubuğundaki menüleri; JMenuItem da menülerdeki öğeleri temsil eder. JCheckBoxMenuItem ve JRadioButtonMenuItem nesneleri, daha önce öğrendiğimiz JCheckBox ve JRadioButton nesnelerine çok benzer. Aralarındaki tek fark, JCheckBoxMenuItem ve JRadioButtonMenuItem'ların menülere eklenmesidir. Ayrıca bunlara ek olarak, bu örneğimizde JColorChooser sınıfını da kullanacağız. JColorChooser, kullanıcıya bir renk paleti sunarak bir renk seçmesini sağlayan bir bileşendir. Çoğu zaman çok işe yarar ve biz programcılar için büyük bir kolaylık getirir. Kullanacağımız bileşenleri kısaca açıkladıktan sonra, artık ilk programımızın işlevine geçebiliriz. Genel olarak, menü çubuğumuzda 3 adet menümüz olacak. Bunlardan birincisi “Dosya” menüsüdür. Bunun altında arka plan rengini değiştirmeye ve pencereyi kapatmaya yarayan seçenekler yapacağız. Seçenekler sadece yazıdan oluşmak zorunda değildir. Seçeneklere resim de eklenebilir. Bunu göstermek için “Dosya” menüsünde    bir adet de küçük resmimiz olacak. “Check Box” menümüzün altında 2 adet JCheckBoxMenuItem'ımız, “Radio Button” menümüzün altında da 3 adet JRadioButtonMenuItem'ımız olacak. Bunlardan herhangi biri seçildiğinde veya seçimi kaldırıldığında, bunu JLabel'larla göstereceğiz. Dosya menüsündeki resmin görüntülenebilmesi için yandaki resmi programınızla aynı klasöre papatya.jpeg isminde kaydetmelisiniz. Şimdi kaynak kodumuzu verelim.

MenuOrnek.java: 
import java.awt.*;
import java.awt.event.*; import javax.swing.*;
public class MenuOrnek extends JFrame {
private JLabel check1, check2, radio1, radio2, radio3;
private JMenuBar menuBar;
private JMenu dosya, check, radio;
private JMenuItem renk, cicek, kapat;
private JCheckBoxMenuItem ch1, ch2;
private JRadioButtonMenuItem rdb1, rdb2, rdb3;
private ButtonGroup bg; private Color arkaPlan = null;


public MenuOrnek(){
setSize(300,300);
setTitle("JMenuBar Ornegi");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(null);
menuBar = new JMenuBar();
dosya = new JMenu("Dosya");
dosya.setMnemonic(KeyEvent.VK_D);
menuBar.add(dosya);
renk = new JMenuItem("Arkaplan...");
renk.setMnemonic(KeyEvent.VK_A);
dosya.add(renk);
renk.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
Color r = JColorChooser.showDialog(null,"Arkaplan Rengini Secin", arkaPlan);
if (r != null)

arkaPlan = r;
getContentPane().setBackground(r);
} });

cicek = new JMenuItem(new ImageIcon("papatya.jpeg"));
dosya.add(cicek);
dosya.addSeparator();
kapat = new JMenuItem("Kapat");
kapat.setMnemonic(KeyEvent.VK_K);
dosya.add(kapat);
kapat.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
System.exit(0);
}
});

check = new JMenu("Check Box");
check.setMnemonic(KeyEvent.VK_C);
menuBar.add(check);
ch1 = new JCheckBoxMenuItem("Check Box 1");
ch1.setMnemonic(KeyEvent.VK_1);
check.add(ch1);
ch1.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
if (e.getStateChange() == ItemEvent.SELECTED)
check1.setText("JCheckBoxMenuItem 1 secili");
else
check1.setText("JCheckBoxMenuItem 1 secili degil");
}
});

ch2 = new JCheckBoxMenuItem("Check Box 2");
ch2.setMnemonic(KeyEvent.VK_2);
check.add(ch2);
ch2.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
if (e.getStateChange() == ItemEvent.SELECTED)
check2.setText("JCheckBoxMenuItem 2 secili");
else
check2.setText("JCheckBoxMenuItem 2 secili degil");
}
});

radio = new JMenu("Radio Button");
radio.setMnemonic(KeyEvent.VK_R);
menuBar.add(radio);
bg = new ButtonGroup();
rdb1 = new JRadioButtonMenuItem("Radio Button 1");
rdb1.setSelected(true);
rdb1.setMnemonic(KeyEvent.VK_1);
radio.add(rdb1);
bg.add(rdb1);
rdb1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
radio1.setText("JRadioButtonMenuItem 1 secili");
radio2.setText("JRadioButtonMenuItem 2 secili degil");
radio3.setText("JRadioButtonMenuItem 3 secili degil");
});}

rdb2 = new JRadioButtonMenuItem("Radio Button 2");
rdb2.setMnemonic(KeyEvent.VK_2); radio.add(rdb2);
bg.add(rdb2);
rdb2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
radio1.setText("JRadioButtonMenuItem 1 secili degil");
radio2.setText("JRadioButtonMenuItem 2 secili");
radio3.setText("JRadioButtonMenuItem 3 secili degil");
});}

rdb3 = new JRadioButtonMenuItem("Radio Button 3");
rdb3.setMnemonic(KeyEvent.VK_3); radio.add(rdb3);
bg.add(rdb3);
rdb3.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
radio1.setText("JRadioButtonMenuItem 1 secili degil");
radio2.setText("JRadioButtonMenuItem 2 secili degil");
radio3.setText("JRadioButtonMenuItem 3 secili");
});}

setJMenuBar(menuBar);
check1 = new JLabel("JCheckBoxMenuItem 1 secili degil");
check1.setBounds(20,20,250,20); getContentPane().add(check1);
check2 = new JLabel("JCheckBoxMenuItem 2 secili degil");
check2.setBounds(20,60,250,20); getContentPane().add(check2);
radio1 = new JLabel("JRadioButtonMenuItem 1 secili");
radio1.setBounds(20,100,250,20); getContentPane().add(radio1);
radio2 = new JLabel("JRadioButtonMenuItem 2 secili degil");
radio2.setBounds(20,140,250,20); getContentPane().add(radio2);
radio3 = new JLabel("JRadioButtonMenuItem 3 secili degil");
radio3.setBounds(20,180,250,20); getContentPane().add(radio3);
setVisible(true);
}

public static void main(String[] args)
{
new MenuOrnek();
}
}

Kodu birlikte inceleyelim.

getContentPane().setLayout(null);

Önceki örneklerimizden farklı olarak, bu örneğimizde penceremizin ContentPane'inin Layout'unu null yapıyoruz. Bu, elemanların ekrandaki yerleşimleri için bir yerleşim düzenleyicisi kullanmayacağız, bunu kendimiz ayarlayacağız demektir. Bunun avantajı, bileşenlerin ekranda tam olarak istediğimiz yerde görüntülenebilmesini sağlamaktır. Dezavantajı ise büyük programlarda programcıya getireceği zorluktur. Bir yerleşim düzenleyicisi (Layout Manager) olmadan bileşenlerin konumlarını ayarlamak uğraştırıcı olabilir. Ancak bizim örneğimizde menüdekiler haricinde çok fazla bileşen olmadığı için bizim için bir problem yaratmayacak.

menuBar  =  new  JMenuBar();

Menü çubuğumuzu temsil eden JMenuBar nesnemizi oluşturuyoruz.

 dosya  =  new  Jmenu("Dosya");
dosya.setMnemonic(KeyEvent.VK_D); menuBar.add(dosya);

“Dosya” menümüzü oluşturuyoruz ve klavye kısayolunu atıyoruz. KeyEvent sınıfının içindeki VK_D sabiti, klavyedeki “D” tuşuna karşılık gelir. JMenu'deki setMnemonic fonksiyonuyla bir menünün klavye kısayolu belirlenebilir. Bu sayede menüye fareyle tıklamak yerine klavye kısayolunu kullanarak da erişebileceğiz. Son olarak da, “Dosya” menümüzü menü çubuğuna ekliyoruz.

renk.addActionListener(new ActionListener() 
{
public void actionPerformed(ActionEvent e){
Color r = JColorChooser.showDialog(null, "Arkaplan Rengini Secin", arkaPlan);
if (r != null)
{
arkaPlan = r;
}
getContentPane().setBackground(r);
}
});

Arkaplan rengini değiştirmek için kullanacağımız seçeneğimize ActionListener'ı ekliyoruz. Arkaplan seçeneği seçildiği zaman, eklediğimiz ActionListener'daki actionPerformed fonksiyonu çalışacaktır. JcolorChooser sınıfında tanımlı    statik showDialog fonksiyonu, ekranda bir renk paleti oluşturacak ve kullanıcının bir renk seçmesini sağlayacaktır. Bu fonksiyon argüman olarak sırayla, pencerenin parent bileşenini, pencerede görüntülenecek başlığı ve öntanımlı olarak göstereceği rengi alır ve sonrasında seçilen rengi döner. Eğer kullanıcı bir renk seçmeden “Cancel” tuşuna basarsa veya köşedeki kapatma düğmesine tıklayarak pencereyi kapatırsa fonksiyon boşluk (null) dönecektir. Fonksiyonun çıktısı soldaki gibidir.

 cicek  =  new  JMenuItem(new  ImageIcon("papatya.jpeg"));

“Dosya” menüsünde görüntülenecek resmimizi oluşturuyoruz. Bunu yapmak için JMenuItem'ın yapıcı fonksiyonunu String yerine bir ImageIcon nesnesiyle çağırmak yeterli olacaktır.

dosya.addSeparator();

JMenu'de tanımlı addSeparator() fonksiyonu, menüdeki öğelerin arasına yatay bir ayırıcı koymak için kullanılır. Bu, menüdeki öğeleri birbirinden ayırmak için kullanılan basit ama etkili bir yoldur.

ch1.addItemListener(new  ItemListener(){
public void itemStateChanged(ItemEvent e){
if (e.getStateChange() == ItemEvent.SELECTED)
check1.setText("JCheckBoxMenuItem 1 secili");
else
check1.setText("JCheckBoxMenuItem 1 secili degil");
}
});

JCheckBoxMenuItem'ımıza ItemListener ekliyoruz. Bunu yaptığımız zaman, JCheckBoxMenuItem (ya da JCheckBox) seçildiğinde ya da seçimi kaldırıldığında bir ItemEvent ateşlenir ve Itestener'daki ItemStateChanged fonksiyonu çalışır. Ateşlenen ItemEvent'teki getStateChange() fonksiyonunun döndürdüğü değere bakılarak JCheckBoxMenuItem'ın (ya da JCheckBox'ın) seçildiği ya da seçiminin kaldırıldığı anlaşılabilir. Bu fonksiyon, JCheckBoxMenuItem seçilmişse ItemEvent.SELECTED, seçimi kaldırılmışsa ItemEvent.DESELECTED sabitini dönecektir.

setJMenuBar(menuBar);

JFrame'deki setJMenuBar fonksiyonuna hazırladığımız menü çubuğunu (JMenuBar nesnesini) göndererek pencerenin menü çubuğunu görüntülemesini sağlıyoruz. Belirtmekte fayda var, tanımladığımız sınıf (MenuOrnek), Jframe'den türetildiği için bu fonksiyonu da üst sınıf olan JFrame'den alıyor.

check1.setBounds(20,20,250,20);

Hatırlarsanız, penceremizin ContentPane'inin Layout'unu “null” yapmıştık. Bu yüzden, derleyiciye, bileşenlerimizi ekranın neresinde görüntülemek istediğimizi bildirmemiz gerekiyor. Bunu, bileşendeki setBounds fonksiyonuyla yapabiliyoruz. Fonksiyona gönderdiğimiz değerler sırasıyla bileşenin x ve y koordinatlarını, genişliğini ve yüksekliğini piksel cinsinden belirtir. Kullandığımız koordinat sisteminde ekranın sol üst köşesi (0,0) noktasıdır. Soldan sağa doğru x bileşeni artarken, yukarıdan aşağıya doğru y bileşeni artar.

   Bu örneğimizle programlarımıza nasıl menü çubuğu ekleyebileceğimizi öğrendik. Tavsiyem, arayüzünüz basit de olsa menü çubuğu kullanın. Bu sayede daha düzenli arayüzler oluşturabilirsiniz. Sıradaki ve son örneğimizle klavye girişiyle ortaya çıkan KeyEvent'leri yönetmeyi ve diyalog penceresi oluşturmayı öğreneceğiz.

Son örneğimiz bir önceki kadar uzun ve karışık değil. Sifre.java isimli bu programda kullanıcıdan şifresini girmesini isteyeceğiz. Kullanıcı tuşlara basmaya başladığı anda girdiği şifreyi kontrol edip, doğru olup olmadığına bakacağız. Kullanıcının şifresini girmesi için bir JPasswordField kullanacağız. JPasswordField, JTextField'dan türetilmiş bir bileşendir ve    JTextField'a çok benzer. JPasswordField'ı JTextField'dan ayıran en önemli özellik, girilen bilginin ekranda olduğu gibi görüntülenmemesidir. Bir JPasswordField'da girilen harfler öntanımlı olarak '*' karakteri eklinde görüntülenir. Fakat bunu değiştirme yetkisine sahibiz. Örneğimizde klasik olan '*' karakterini '@' ile değiştireceğiz. Programımız ayrıca, kullanıcı kapatma butonuna tıkladığı anda sonlanmak yerine, kullanıcıyla iletişime geçip,         onay aldıktan sonra sonlanacak. Bunu yapmak için de JOptionPane sınıfını kullanacağız. Resimlerin görüntülenebilmesi için yandaki resimleri sırasıyla soru.jpeg, hata.jpeg ve tamam.jpeg isimleriyle programınızla aynı dizine kaydetmelisiniz.

Simdi, programımızın kaynak koduna göz atalım.

import java.awt.*; import java.awt.event.*;
import javax.swing.*;
public class Sifre extends JFrame {
private JPasswordField sifreAlani; private String sifre = "ThereIsNoSpoon";
private JLabel resim, bilgi;
public Sifre()
{
setTitle("Sifre");
setSize(350,150);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
getContentPane().setLayout(new FlowLayout());
sifreAlani = new JPasswordField(20);
sifreAlani.setEchoChar('@');
getContentPane().add(sifreAlani);
sifreAlani.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e) {
if(new String(sifreAlani.getPassword()).equals(sifre))
{
resim.setIcon(new ImageIcon("tamam.jpeg"));
bilgi.setText("Sifre dogru.");
}
else
{
resim.setIcon(new ImageIcon("hata.jpeg"));
bilgi.setText("Sifre yanlis.");
}
}
public void keyTyped(KeyEvent e){ }
});
resim = new JLabel(new ImageIcon("soru.jpeg"));
getContentPane().add(resim);
bilgi = new JLabel("Lutfen sifrenizi girin.");
getContentPane().add(bilgi);
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
int secim = JOptionPane.showConfirmDialog(null, "Cikmak istediginizden emin misiniz?",
"Kapat", JOptionPane.YES_NO_OPTION);
if (secim == JOptionPane.YES_OPTION)
System.exit(0);
}
});
setVisible(true);
}
public static void main(String[] args){
new Sifre();
}
}

Kodumuza göz atalım.

setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

Şimdiye kadarki tüm örneklerimizde JFrame'in setDefaultCloseOperation fonksiyonunu JFrame.EXIT_ON_CLOSE ile çağırmıştık. Bunu yapmamızın nedeni, pencerenin köşesindeki kapatma butonuna tıklandığında programın sonlanacak olmasıydı. Bu örneğimizde ise bu fonksiyonu JFrame.DO_NOTHING_ON_CLOSE ile çağırıyoruz. Bu, adından da anlaşılacağı gibi, penceredeki kapatma butonuna tıklandığında hiç bir etki olmayacak. Yani pencere kapanmayacak. Bunun sebebini birazdan açıklayacağım.

sifreAlani  =  new  JPasswordField(20);

Kullanıcının şifre gireceği JPasswordField'ı oluşturuyoruz. Yapıcı fonksiyona gönderilen argüman, tıpkı JTextField'da olduğu gibi, bileşenin genişliğini belirtiyor.

sifreAlani.setEchoChar('@');

JPasswordField'ın setEchoChar fonksiyonuna '@' karakterini göndererek, şifre alanında '*' yerine '@' görüntülenmesini sağlıyoruz.

sifreAlani.addKeyListener(new  KeyListener(){
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e) {
if ( new String(sifreAlani.getPassword()).equals(sifre) )
{
resim.setIcon(new ImageIcon("tamam.jpeg"));
bilgi.setText("Sifre dogru.");
}
else
{
resim.setIcon(new ImageIcon("hata.jpeg"));
bilgi.setText("Sifre yanlis.");
}
public void keyTyped(KeyEvent e) { }
});

JPasswordField'ımıza KeyListener ekleyerek, kullanıcının klavyeden giriş yapması durumunda KeyEvent ateşlenmesini sağlıyoruz. KeyListener arayüzünde (Interface) tanımlı 3 adet fonksiyon vardır. Biz bunlardan sadece keyReleased fonksiyonunu kullanacağız. Bu fonksiyon, kullanıcı bir tuştan elini çektiği anda çağırılır. Diğer fonksiyonları kullanmayacak olmamıza rağmen arayüzde belirtilen tüm fonksiyonları tanımlamak zorundayız. Biz de diğerlerini boş tanımlıyoruz. JPasswordField'daki getPassword() fonksiyonu, şifre alanına girilen veriyi char[] eklinde döndürür. Bu Array'i String'e çevirip elimizdeki şifreyle String'ler için eşitlik fonksiyonunu kullanarak karşılaştırıyoruz. Bu noktada yapmak istediğim bir hatırlatma var. Genelde C++'dan Java'ya geçen programcıların (ben dahil :) düştükleri bir hata, Java'da String'leri == operatörüyle kıyaslamaktır. Bu operatör, C++'da aşırı yüklenmiş (overloaded) olduğu için iki String nesnesini karşılaştırır, fakat Java'da bu operatör String'lerin içeriğini değil, referanslarını karşılaştırır. Bu yüzden Java'da String'leri karşılaştırmak için her zaman “equals” fonksiyonunu kullanın.

addWindowListener(new WindowAdapter(){ 
public void windowClosing(WindowEvent e){
int secim = JOptionPane.showConfirmDialog(null, "Cikmak istediginizden emin misiniz?",
"Kapat", JOptionPane.YES_NO_OPTION);
if (secim == JOptionPane.YES_OPTION)
System.exit(0);
}
});

JFrame'imize bir WindowListener ekliyoruz. WindowListener, pencerenin kapatılması, simge durumuna küçültülmesi gibi olayları yöneten arayüzdür. İçinde çok sayıda fonksiyon vardır. Normalde addWindowListener'a bir WindowListener gönderip, arayüzde belirtilen tüm fonksiyonları tanımlamamız gerekirdi. Bunu yapabilirsiniz, fakat biz sadece windowClosing fonksiyonunu kullanacağız ve diğer fonksiyonları, içleri boş bile olsa uzun uzun tanımlamak istemiyoruz. Bu amaçla, WindowListener'ı tamamlayan soyut (abstract) bir sınıf olan WindowAdapter'ı kullanıyoruz. Bu sayede yalnızca ilgilendiğimiz fonksiyonu tanımlayacağız. Diğer    fonksiyonlar otomatik olarak boş tanımlanmış oluyor. WindowClosing fonksiyonunun içinde, JOptionPane'deki statik showConfirmDialog fonksiyonunu çağırıyoruz. Bu fonksiyon, kullanıcıya bir onay penceresi oluşturuyor. Fonksiyona gönderilen argümanlar sırasıyla, pencerenin üst bileşenini, pencerede görünecek mesajı, pencerenin başlığını ve hangi seçeneklerin görüntüleneceğini belirliyor. Fonksiyon, kullanıcının seçtiği seçeneğe göre bir tamsayı dönüyor. SetDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE) yapmamızın sebebi, kullanıcının, açılan pencerede “No”yu seçmesi ya da pencereyi, köşedeki butona tıklayarak kapatması durumunda pencerenin kapanmayacak olmasıdır. Kullanıcı “Yes”i seçerse de, System.exit(0) yaparak programımızı kendimiz sonlandırıyoruz. Oluşturduğumuz onay penceresi, soldaki gibi görüntüleniyor.

Sonuç

Sonuç olarak, bu son örneğimizle, Eylül ayında başladığımız Swing rehberimizin sonuna gelmiş bulunuyoruz. Yazdığımız onca koda, öğrendiğimiz onca şeye rağmen, Swing'in bize sunduğu ve burada tanıma fırsatı bulamadığımız daha pek çok imkan bulunmaktadır. Bunları kendi başınıza araştırıp öğrenmenizi şiddetle tavsiye ederim. Üç ay boyunca elimden geldiğince faydalı ve açıklayıcı olmaya çalıştım. Umarım başarılı olmuşumdur ve konu ilginizi çekmiştir. Hepinize iyi çalışmalar, iyi günler dilerim.

Görüşmek üzere :)

Kaynaklar:



Tolga Akın
- 5 -