Bir kaç haftadır Java dilini popüler yapan Applet'
ler ile uğraşıyorum. Buna karşın günümüz dünyasının bir programlama dilinden
bekledikleri arasında mutlaka windows uygulamalarının var olması gerektiğini
düşünüyorum. Sonuç olarak Applet' ler her ne kadar çok başarılı olsalarda, zaman
zaman windows uygulamaları geliştirmemizde gerekiyor. Bir windows uygulamasının
belkide en temel özelliği mutlaka bir Form (Nam-ı diğer pencere diyebiliriz)
ekranına sahip olması. Peki java dilinde, windows uygulamları oluşturmak için
nasıl bir yol izlemem gerekir. İşte bu hafta boyunca, java dili ile bağımsız
olarak çalışabilen pencereleri incelemeye çalıştım.
Sun' ın Java paltformu, Microsoft' un ciddi
rakiplerinden birisi. Belkide tek ciddi rakibi. Ancak bu rekabet zaman zaman
biraz komik olaylarada neden olmuyor değil. Örneğin, yaptığım araştırmalarda
gördüm ki, Windows uygulamarında Form kavramı, java dilinde Frame olarak
adlandırılıyor. Bu kısa politik düşüncelerden sonra, artık ilk form ekranımı,
pardon düzeltiyorum; ilk frame ekranımı tasarlamam gerektiğine karar verdim. Bu
amacımı gerçekleştirebilmek amacıyla aşağıdaki çok kısa uygulamayı yazdım.
import java.awt.*;
public class IlkPencere
{
public static void main(String args[])
{
Frame pencere=new Frame("ILK
PENCEREM");
pencere.setLocation(0,0);
pencere.setBackground(Color.red);
pencere.setVisible(true);
}
} |
Yazdığım bu java dosyasının derledikten sonra
çalıştırdım. Karşımda beni bekleyen güzel bir pencere olacağı düşüncesindeydim.
Gerçektende muazzam bir pencere oluşturmayı başarmıştım :)

Doğruyu söylemek gerekirse daha büyük bir frame
olacağını düşünmüştüm. Bunun üzerine yazmış olduğum kod satırlarını incelemeye
başladım. İlk olarak awt.window paketinde yer alan Frame sınıfından bir nesne
örneği oluşturmuştum. Bunu yaparkende, yapıcı metoda string tipte bir parametre
gönderdim. Bu parametre Frame penceresinin başlığı (Title) olacaktı.
| Frame pencere=new Frame("ILK
PENCEREM"); |
Daha sonra, Frame' in ekran üzerindeki konumunu
belirledim. Bunun içinde setLocation metoduna X ve Y koordinatlarını 0 olarak
verdim. Böylece, Frame penceresi ekranın sol üst köşesinde konumlanacaktı.
| pencere.setLocation(0,0); |
setBackgorund metodu ile Frame penceresinin arka
plan rengini kırmızı olarak belirledim.
| pencere.setBackground(Color.red); |
Frame sınıfının en önemli metodu ise setVisible
idi. Bu metod, oluşturulan Frame penceresinin gösterilmesini sağlıyordu. Bunun
için parametre olarak metoda true değerini vermek yeterliydi.
| pencere.setVisible(true); |
Buraya kadar herşey sorunsuz gözüküyordu. Ancak
Frame' in neden böyle göründüğünü tam olarak anlayamamıştım. Kaynaklarımı gözden
geçirdiğimde, setSize isimli metodu kullanmadığımı farkettim. Bu metod ile
Frame' in başlangıç boyutlarını belirleyebiliyordum. Şimdi tek yapmam gereken
uygulama koduna setSize metodunu ilave etmek olacaktı. Lakin ufak bir sorun
vardı. O da, Frame penceresini X butonuna basıp kapatamıyor oluşuydu. Programdan
çıkamıyordum. Bunun tek bir nedeni olabilirdi o da, X butonu ile kapatma işlemi
için gerekli olan olay dinleyecisinin ilgili olay metodunu çalıştırmayışıydı.
Frame sınıfının olaylarına sonradan zaten
bakacaktım. Ancak bu pencereyi bir şekilde kapatıp, kodumu düzenlemek
istiyordum. Yaklaşık bir yarım saat kadar sırf bu pencerenin nasıl
kapatılacağını araştırdım. Nitekim windows' un ALT+F4 tuş kombinasyonu dahi işe
yaramıyordu. Sonunda komut satırından CTLR+C tuş komimasyonuna basmam
gerektiğini öğrendim. Bu tuş kombinasyonu sayesinde açık olan uygulama
kapatılabiliyordu. Artık uygulama kodlarımı düzenleyebilir ve Frame penceresinin
istediğim boyutlarda oluşturulmasını sağlayabilirdim. Bu amaçla kodlarıma
aşağıdaki satırı ekledim. Burada ilk parametre Frame penceresinin genişliğini
(width), ikinci parametres ise yüksekliğini (height) belirtmekteydi.
| pencere.setSize(300,100); |
Uygulamayı bu haliyle derleyip çalıştırdığımda
300 piksel genişliğinde ve 100 piksel yüksekliğinde bir Frame penceresi elde
ettim. Artık hem Title görünüyordu, hemde Frame penceresi daha makul
boyutlardaydı.

Kaynaklardan Frame ile ilgili olarak
kullanabileceğim diğer teknikleride araştırmaya başladım. Örneğin, X butonunun
aksine, Minimize ve Maksimize butonları çalışıyor dolayısıyla Frame penceresi
mimimize edilebiliyor yada maksimize olabiliyordu. Derken aklıma, bu Frame' in
Maksimize edilmek istendiğinde, belirli yükseklik ve genişliğin üstüne
çıkmamasını nasıl sağlayabileceğim sorusu geldi. Bunun için setMaximizedBounds()
isimli bir metod buldum. Bu Frame sınıfına ait metoda Rectangle sınıfı türünden
bir nesne parametre olarak aktarılabiliyordu. Bu Rectangle nesnesi, bir dortgen
şeklini boyutları ve konumları ile bildirebildiğinden, setMaximizedBounds metodu
sayesinde, Frame penceresi belirtilen Rectangle nesnesinin boyutları kadar
büyüyebilecekti. Hemen bu durumu analiz etmek amacıyla uygulama kodlarını
aşağıdaki gibi geliştirdim.
Rectangle r=new Rectangle(500,500);
pencere.setMaximizedBounds(r); |
Burada Rectangle sınıfından nesne örneğini
oluştururken, parametre olarak genişlik ve yüksekliği bildirdim. İlk parametre
Rectangle nesnesinin genişliğini, ikinci parametre ise yüksekliğini
belirtmekteydi. Daha sonra, setMaximizedBounds metoduna, bu Rectangle nesnesini
parametre olarak verdim. Uygulamayı tekrar derleyip çalıştırdığımda ve Maksimize
butonuna bastığımda, Frame' in 500 piksel X 500 piksel boyutlarına geldiğini
gördüm. Normal şartlar altında bu metodu kullanmasaydım, Frame tüm ekranı
kaplayacak şekilde boyutlandırılacaktı.
Frame pencereleri ile ilgili aklıma takılan bir
diğer nokta ise, X butonu ile pencereyi kapatamayışımdı. Bunu kendim
programlamam gerekiyordu. Bir başka deyişle, olay metodunu yazmalıydım.
Kaynaklarımı araştırdığımda, Java Frame sınıfının aşağıdaki window olay
metodlarına cevap verebildiğini öğrendim.
|
Frame için Window
Olayları |
|
windowOpened |
Pencere ilk kez gösterildiğinde çalışan
olay. |
WindowListener Arayüzünden |
|
windowClosing |
Pencere
kullanıcı tarafından kapatılırken gerçekleşen olay. |
|
windowClosed |
Pencere kapatıldıktan sonra çalışan olay. |
|
windowIconified |
Pencere
minimize edildiğinde gerçekleşen olay. |
|
windowDeiconified |
Minimize
olan bir Pencere normal haline döndüğünde gerçekleşen olay. |
|
windowActivated |
Pencereya odaklanıldığı (Focus) yani aktifleştirildiği zaman çalışan
olay. |
|
windowDeactivated |
Pencereden ayrılındığında çalışan olay. |
|
windowLostFocus |
Focus (odak) pencereden uzaklaştığında
çalışan olay. |
WindowFocusListener Arayüzünden |
|
windowGainedFocus |
Odak (Focus) pencereye geldiğinde çalışan
olay. |
|
windowStateChanged |
Pencerenin durumu değiştiğinde (minimize edildiğinde, maksimize
edildiğinde vb.) çalışan olay. |
WindowStateListener Arayüzünden |
İlk olarak denemek istediğim, pencerenin X butonu
ile kapatılabilmesiydi. Öncelikle, windowClosing metodunu uygulamam
gerekiyordu. Bunu gerçekleştirebilmek için, WindowListener arayüzünü sınıfa
uygulamalıydım. Böylece, WindowListener arayüzünden uyguladığım windowClosing
metodunda yazabilir ve X butonu ile pencerenin kapatılaması sırasında oluşacak
olayı kodlayabilirdim. Bu amaçla sınıf kodlarını aşağıdaki gibi geliştirdim.
import java.awt.*;
import java.awt.event.*;
public class IlkPencere implements WindowListener
{
public static void main(String args[])
{
IlkPencere p=new IlkPencere();
Frame pencere=new Frame("ILK
PENCEREM");
pencere.setLocation(0,0);
pencere.setBackground(Color.red);
pencere.setSize(300,100);
Rectangle r=new Rectangle(500,500);
pencere.setMaximizedBounds(r);
pencere.addWindowListener(p);
pencere.setVisible(true);
}
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
} |
Programı bu haliyle derlediğimde aşağıdaki hata
mesajını aldım.

Anladığım kadarı ile WindowListener arayüzündeki
tüm window olay metodlarını sınıf içerisinde kullanmasamda bildirmeliydim. Bu
amaçla sınıfa aşağıdaki metodlarıda ekledim.
public void windowOpened(WindowEvent
e)
{
}
public void windowClosed(WindowEvent e)
{
}
public void windowIconified(WindowEvent e)
{
}
public void windowDeiconified(WindowEvent e)
{
}
public void windowActivated(WindowEvent e)
{
}
public void windowDeactivated(WindowEvent e)
{
} |
Uygulama başarılı bir şekilde derlendikten sonra,
hemen X butonu ile kapatılıp kapatılamadığını denemedim. Sonuç başarılıydı.
Elbetteki bir pencere bu haliyle çok yavan durmaktaydı. Bu pencereye kontroller
eklemek gerekiyordu. Normal bir Applet' e kontroller nasıl ekleniyorsa buradada
aynı kurallar geçerliydi. Bu kez bir Applet' e kontrol eklemek yerine bir Frame
nesnesine kontrol ekleyecektim. Bu amaçla uygulamayı biraz daha düzenlemeye ve
ilginç hale getirmeye karar verdim. Amacım Frame içindeki bir button yardımıyla
başka bir frame penceresinin açılabilmesini sağlamaktı. Bu amaçla aşağıdaki
örneği oluşturdum.
import java.awt.*;
import java.awt.event.*;
public class IlkPencere implements WindowListener,ActionListener
{
public Frame p1;
public Button btnIkinciPencere;
public Button btnKapat;
public int X;
public int Y;
public void PencereAyarla(String baslik,int genislik,int
yukseklik,int konumX, int konumY,Color
arkaPlanrengi)
{
X=konumX;
Y=konumY;
p1=new Frame(baslik);
p1.setLocation(konumX,konumY);
p1.setBackground(arkaPlanrengi);
p1.setSize(genislik,yukseklik);
p1.setLayout(new FlowLayout());
p1.addWindowListener(this);
btnIkinciPencere= new Button("Ikinci Pencere");
btnKapat=new Button("Kapat");
p1.add(btnIkinciPencere);
p1.add(btnKapat);
btnKapat.addActionListener(this);
btnIkinciPencere.addActionListener(this);
p1.setVisible(true);
}
public static void main(String args[])
{
IlkPencere p=new IlkPencere();
p.PencereAyarla("ANA
PENCERE",250,100,0,0,Color.white);
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==btnKapat)
{
p1.setVisible(false);
}
else
if(e.getSource()==btnIkinciPencere)
{
IlkPencere
p=new IlkPencere();
X=X+50;
Y=Y+50;
p.PencereAyarla("ANA PENCERE",100,100,X,Y,Color.red);
}
}
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
public void windowOpened(WindowEvent e)
{
}
public void windowClosed(WindowEvent e)
{
}
public void windowIconified(WindowEvent e)
{
}
public void windowDeiconified(WindowEvent e)
{
}
public void windowActivated(WindowEvent e)
{
}
public void windowDeactivated(WindowEvent e)
{
}
} |
Bu uzayıp giden kodlar çok işe yaramıyor. Ancak şu
ana kadar GUI 'ler ile ilgili bilgilerimi tekrar etmemede yardımcı oldu. Bu
uygulama çalıştığında ilk olarak belirtilen boyutlarda, konumda, başlıkta ve art
alan renginde bir ana pencere oluşturuyor. Bu pencere üzerine, FlowLayout
sınıfının öngördüğü Layout düzenine göre yerleşen iki Button bileşenim var.
Ikınci Pencere başlıklı button bileşenine tıklandığında yeni bir pencere
oluşturuluyor. Kapat button bileşeni ise, bu pencereyi kapatıyor. Bu kapatma
işleminde setVisible(false) metodunu kullandım. Böylece sonradan açılan
pencereler aslında gizleniyordu.
Uygulamayı bu haliyle derleyip çalıştırdığımda
aşağıdaki gibi bir görüntü oluştu. Her yeni pencere bir öncekinin konumunun 50
birim sağına ve altına konumlandırılıyor. Elbette X butonuna basıldığında
System.exit(0) metodu o an çalışan prosesi sonlandırdığı için tüm pencereler
kapanmaktaydı. Mesela ilk pencerede Kapat başlıklı butona basınca komut satırı
açık kalacak şekilde pencere ortadan kayboluyor. Yani görünmez oluyor. Ancak
proses çalışmaya devam ediyor. Sanırım neden işe yaramaz bir program olduğu
ortada. Olsun en azından el cimnastipi yapmış oldum.

Şu anada kadar yaptıklarım ile geliştirdiğim bu
pencere uygulamalarında önemli bir sorun var aslında. Bu uygulamaları
çalıştırabilmek için komut satırında ilgili sınıfı java yorumlayıcısı ile açmam
gerekiyor. Diğer taraftan uygulama çalışırken, komut satırı açık kalıyor. Oysaki
normal bir exe dosyası gibi bu uygulamanın tek başına çalışabilmesi çok daha
yerinde olur. İşte bunu gerçekleştirmek için kaynaklarda 3 yoldan bahsedildiğini
öğrendim. En basit olanı üçüncü parti yazılımlar ile bu işi gerçekleştirmek.
Örneğin halen daha özlemini çektiğim değerli arkadaşımın bilgisayarında yer alan
JBuilder gibi.
Diğer iki yol ise bizim manuel olarak
kullanabileceğimiz teknikler içeriyor. Bunlardan birisi Dos ortamından kalma
bat(batch) uzantılı dosyalar içerisine uygulamayı çalıştıracak kod satırını
yazmak. Diğeri ise, GUI uygulamasına ati tüm sınıfları ve gerekli dosyaları
içeren bir JAR paketi oluşturmak. Açıkçası JAR paketini oluşturmak bana daha
mantıklı göründü. Ancak bir JAR paketini oluşturmadan önce, bu JAR paketi için
verisyon numarası, ana sınıf gibi bilgileri içeren bir manifesto dosyası
hazırmamam gerektiğini öğrendim. Bu manifesto dosyası, mf uzantılı olmakla
birlikte, aslında .net assembly' larındaki manifesto bilgilerinin tutulduğu
yapıya benzer bir içeriğe sahip. Çok basit olarak geliştridiğim java uygulaması
için aşağıdaki bilgileri içeren bir manifesto dökümanı hazırladım.
Manifest-Verison: 1.0
Main-Class: IlkPencere
Created-By: 1.4.1 (Sun Microsystems Inc.) |
Bu dosyayı Manifesto.mf ile kaydettikten sonra
aşağıdaki komut ile, Jar dosyasını oluşturdum.


Artık Pencereler.Jar dosyasına çift tıkladığımda
GUI uygulamasının, normal bir windows uygulaması gibi çalıştığını gördüm. Bu
sorunu çözmem son derece önemli idi. Artık windows tabanlı GUI' lerin nasıl
oluşturulduğunu, window olaylarına nasıl cevap verdiğini biliyordum. Dahası bu
pencereler üzerine awt bileşenlerinin nasıl ekleneceğini ve herşeyden önemlisi
bu GUI uygulamasının çift tıklamalı versiyonunun Jar dosyası olarak nasıl
hazırlanabileceğini biliyordum. Artık tüm bu bildiklerimi birleştirerek daha işe
yarar bir uygulama yapabileceğim kanısındaydım. Bunun için aklıma basit bir
hesap makinesi uygulması yazmak geldi. Ama çok basit. Sadece 2 operand değeri
için 4 işlem yapacaktı. Lakin burada önemli olan, Frame' in tasarlanması ve
Frame üzerindeki bileşenlerin olaylara tepki vermesinin sağlanmasıydı. Hemen
kolları sıvadım ve uygulamayı geliştirmeye başladım. Sonuçta hem pratik yapmış
oldum hemde GUI bilgilerimi tekrar etmiş. Sonuçta aşağıdaki küçük programcık
ortaya çıktı.
import java.awt.*;
import java.awt.event.*;
/* HesapMakinesi sınıfında window olaylarına ve Button olaylarına izin
verebilmek için, WindowListener ve ActionListener arayüzlerinin
uygulanması gerekir. */
public class HesapMakinesi implements WindowListener,ActionListener
{
/* Frame sınıfına ait nesne tanımlanıyor ve bu Frame
üzerindeki awt bileşenleri tanımlanıyor.*/
public Frame f;
public Button btnHesapla;
public Label lbSayi1;
public Label lbSayi2;
public Label lbIslem;
public TextField tfSayi1;
public TextField tfSayi2;
public Choice lstIslem;
/* iki sayı değerini ve işlem sonucunu tutacak double
tipinden değişkenler tanımlanıyor.*/
public double sayi1,sayi2,sonuc;
/* Olustur metodunda, penceremiz ve üzerindeki bileşenler
oluşturuluyor.*/
public void Olustur()
{
f=new Frame("Hesap Makinesi"); //
Başlığı (Title) Hesap Makinesi olan bir Frame nesnesi oluşturuluyor.
f.setLayout(new FlowLayout()); //
Frame üzerindeki bileşenler FlowLayout tekniğine göre dizilecekler.
Color c=new Color(248,221,139); /*
Color tipinden bir nesne R (Red), G (Green), B(Blue) formatında
oluşturuluyor.*/
f.setBackground(c); // Pencerenin
arka plan rengi c isimli Color nesnesine göre belirleniyor.
/* TextField bileşenleri 10 karakter
uzunluğunda oluşturuluyor.*/
tfSayi1=new TextField(10);
tfSayi2=new TextField(10);
/* Label bileşenleri başlıkları ile
oluşturuluyor.*/
lbSayi1=new Label("Sayi 1");
lbSayi2=new Label("Sayi 2");
lbIslem=new Label("ISLEMIN
SONUCU...");
/* Button bileşeni oluşturuluyor ve
bu bileşen için olay dinleyicisi ekleniyor.*/
btnHesapla=new Button("Hesapla");
btnHesapla.addActionListener(this);
/* Choice (başka bir deyişle
ComboBox) bileşeni oluşturuluyor. Listedeki elemanlar addItem metodu ile
ekleniyor.*/
lstIslem=new Choice();
lstIslem.addItem("TOPLA");
lstIslem.addItem("CIKART");
lstIslem.addItem("BOL");
lstIslem.addItem("CARP");
/* Bileşenler sırasıyla Frame
bileşenine yani pencereye add metodu ile ekleniyor. */
f.add(lbSayi1);
f.add(tfSayi1);
f.add(lstIslem);
f.add(lbSayi2);
f.add(tfSayi2);
f.add(btnHesapla);
f.add(lbIslem);
f.pack(); /* pack metodu ile
pencerenin yüksekliği ve genişliği, içerdiği bileşenlerin kapladığı
alana göre otomatik olarak ayarlanıyor.*/
f.addWindowListener(this); // Frame
bileşeni için window olay dinleyicisi ekleniyor.
f.setVisible(true); // Frame bileşeni
(pencere) gösteriliyor.
}
/* IslemYap metodunda 4 işlem gerçekleştiriliyor. */
public void IslemYap()
{
sayi1=Double.parseDouble(tfSayi1.getText()); /* TextField bileşenlerinin
string içeriği Double sınıfının parseDouble metodu ile double tipine
dönüştürülerek değişkene atanıyor.*/
sayi2=Double.parseDouble(tfSayi2.getText());
/* if koşullarında Choice bileşeninde
seçili olan item getSelectedItem() metodu ile alınıyor ve uygun olan
işlemler yapılıyor.*/
if(lstIslem.getSelectedItem()=="TOPLA")
{
sonuc=sayi1+sayi2;
bIslem.setText(sayi1+"+"+sayi2+"="+sonuc);
}
else
if(lstIslem.getSelectedItem()=="CARP")
{
sonuc=sayi1*sayi2;
lbIslem.setText(sayi1+"x"+sayi2+"="+sonuc);
}
else
if(lstIslem.getSelectedItem()=="CIKART")
{
sonuc=sayi1-sayi2;
lbIslem.setText(sayi1+"-"+sayi2+"="+sonuc);
}
else
if(lstIslem.getSelectedItem()=="BOL")
{
sonuc=sayi1/sayi2;
lbIslem.setText(sayi1+"/"+sayi2+"="+sonuc);
}
}
public static void main(String args[])
{
HesapMakinesi m=new HesapMakinesi();
m.Olustur();
}
/* actionPerformed olayı meydana geldiğinde bu metod
çalışıyor.*/
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==btnHesapla) // Eğer
olayın kaynağı Button bileşeni ise, yani Button' a tıklandıysa.
{
IslemYap();
}
}
/* Kullanıcı X buton ile pencereyi kapatmak istediğinde bu
olay metodu çalışıyor. */
public void windowClosing(WindowEvent e)
{
System.exit(0); /* Güncel olan proses
sonlandırılıyor. Dolayısıyla uygulama sona eriyor. */
}
public void windowOpened(WindowEvent e)
{
}
public void windowClosed(WindowEvent e)
{
}
public void windowIconified(WindowEvent e)
{
}
public void windowDeiconified(WindowEvent e)
{
}
public void windowActivated(WindowEvent e)
{
}
public void windowDeactivated(WindowEvent e)
{
}
} |
Programı derledikten sonra Manifesto dosyasını
(ManifestoHesapMakinesi.mf) aşağıdaki gibi düzenledikten sonra, JAR Paketinide
hazırladım.
Manifest-Verison: 1.0
Main-Class: HesapMakinesi
Created-By: 1.4.1 (Sun Microsystems Inc.) |

Paketi çift tıkladığımda, basit hesap makinesi
uygulamam kullanılmaya hazırdı.

Artık GUI' lerde iyice ilerlemeye başladığımı
hissediyordum. Bununla birlikte, 2 boyutlu grafik çizimleri, animasyon
hazırlamak, resim işlemek, ses işlemek, Swing bileşenleri, Menu' ler vs... gibi
henüz bilmediğim daha pek çok konu vardı. Ancak hem kahvem hemde pilim bitmişti.
Sanırım önümüzdeki günlerde, bu konulara eğileceğim.
Burak Selim ŞENYURT
selim@bsenyurt.com