Merhaba,
Yakın bir zamanda Java dili ile GUI üzerinde çalışan (arayüzü olan, konsol olmayan) bir örnek uygulama hazırladım. Örnekte 4 adet termometre bulunmakta ve birinin değerini değiştirince ötekiler de güncellenmekte. Dördüncü termometreye göre olan suyun erime ve kaynama noktalarını ise değiştirebiliyorsunuz. Kaynak kodları için yazının devamını okuyabilirsiniz… Örnekten bir resim:
Örnek 4 class’tan oluşmakta: MenuBar, Termometre, TermometreAyarlayici, Uygulama. Kodları mümkün olduğunca comment‘lerle açıklamaya çalıştım. Ancak bu örnek ilk defa GUI uygulaması yapmaya başlamak için uygun değildir, bir miktar GUI bilginizin olması gereklidir.
Maalesef çok “efficient” değil kodlarım, yani kimi yerleri daha iyi olabilirdi. Ama yine de idare eder sanırım.
Termometre Class’ı
import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.SwingConstants; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Line2D; import java.awt.Color; import java.awt.Dimension; // Termometre - Bir termometre'nin bilgisayara uyarlanmış hali public class Termometre extends JPanel { // DEĞİŞKENLER public Uygulama uygulama; public static int ustSinir = 1000; // Termometrenin göstereceği üst sıcaklık limiti public static int altSinir = -1000; // Termometrenin göstereceği alt sıcaklık limiti private int erimeNoktasi, kaynamaNoktasi; private int derece; private String isim; // Termometrenin adı ("Celcius", "Kelvin" vb.) private JLabel ustYazi, altYazi; // GUI ile alakalı, termometre ile direkt alakalı olmayan yazılar private DereceBari termometre; // Termometrenin görsel gösterimini sağlayacak olan panel private Termometre t; // Termometrenin kendisini depolayan bir değişken (inner-class kullanıyor) // çok verimli bir çözüm değil ama malesef böyle gerekti... public Termometre( int erimeNoktasi, int kaynamaNoktasi, String isim ) { t = this; this.erimeNoktasi = erimeNoktasi; this.kaynamaNoktasi = kaynamaNoktasi; this.isim = isim; setLayout( new BorderLayout() ); ustYazi = new JLabel( isim ); altYazi = new JLabel(); termometre = new DereceBari(); // Yazıları ortala ustYazi.setHorizontalAlignment( SwingConstants.CENTER ); altYazi.setHorizontalAlignment( SwingConstants.CENTER ); // Yazıların arkaplanını turuncu yap ustYazi.setBackground( Color.ORANGE ); altYazi.setBackground( Color.ORANGE ); // Görsel termometrenin olduğu panelin arkasını sarı yap termometre.setBackground( Color.YELLOW ); // Yazıların turuncu arkaplanlarının gözükmesi için onları opak yap ustYazi.setOpaque( true ); altYazi.setOpaque( true ); // JLabel component'lerinin ebatlarını ayarla ustYazi.setPreferredSize( new Dimension( getWidth(), 25 ) ); altYazi.setPreferredSize( new Dimension( getWidth(), 40 ) ); // Termometrenin mouse ile tıklanarak değerinin değişmesi için termometreye MouseListener ekle termometre.addMouseListener( termometre ); termometre.addMouseMotionListener( termometre ); // Yazıları ve görsel termometreyi ana panele ekle add( ustYazi, BorderLayout.NORTH ); add( termometre, BorderLayout.CENTER ); add( altYazi, BorderLayout.SOUTH ); } // erimeNoktasi'nın değerini döndürür public int eN() { return erimeNoktasi; } // kaynamaNoktasi'nın değerini döndürür public int kN() { return kaynamaNoktasi; } // derece'nin değerini döndürür public int derece() { return derece; } // erimeNoktasi'nın değerini değiştirir public void eNBelirle( int derece ) { erimeNoktasi = derece; } // kaynamaNoktasi'nın değerini değiştirir public void kNBelirle( int derece ) { kaynamaNoktasi = derece; } // derece'nin değerini değiştirir public void dereceBelirle( int derece ) { this.derece = derece; } // Termometrenin formülünün ve mevcut sıcaklığının olduğu JLabel'ı güncelle // ( boolean ) ? ( true statement ) : ( false statement ) kullanımına dikkat! // Bu ifade alttaki comment içindeki ifadenin kısaltılmış pratik halidir: /* if( boolean ) true statement; else false statement; */ public void paintComponent( Graphics g ) { super.paintComponent( g ); // JLabel'da \n komutu çalışmadığı için bu JLabel'ı 2 satıra ayırabilmek JLabel'ın // içinde biraz HTML kodu kullandım. (En pratik yolu buydu) altYazi.setText( "<html><body><center>" + derece + " " + (char)( 176 ) + "" + isim.charAt( 0 ) + " derece" + "<br>" + " ( " + isim.charAt( 0 ) + " " + ( ( eN() != 0 ) ? ( ( eN() > 0 ) ? ( "- " + eN() + " " ) : ( "+ " + ( -eN() ) + " " ) ) : "" ) + ") / " + ( kN() - eN() ) + "</center></body></html>" ); } // Girilen erimeNoktasi ve kaynamaNoktasi'na sahip olan "derece" sıcaklığındaki bir // termometrenin sıcaklığını baz alarak bu termometrenin sıcaklığını ona uygun şekilde değiştirir public void sicaklikAyarla( int derece, int eN, int kN ) { this.derece = (int)( ( derece - eN ) * 1.0 / ( kN - eN ) * ( this.kN() - this.eN() ) + this.eN() ); } // Termometrenin görselleştirilmiş halini çizmeye yarayan bir inner-class class DereceBari extends JPanel implements MouseListener, MouseMotionListener { // Ekrana termometreyi, onun solunda da referans sıcaklık çizgilerini çizdirir public void paintComponent( Graphics g ) { super.paintComponent( g ); // Bu ifade paintComponent() metodunda olmak zorundadır Graphics2D g2D = (Graphics2D) g; // referans çizgileri daha düzgün çizmek için double // değerlerle çalışmak adına Graphics2D'ye geçiş yap // Önce sol kısımda referans çizgileri göster // REFERANS ÇİZGİLERİ ÇİZİM BAŞLANGIÇ g.drawLine( 0, 0, 0, getHeight() ); int sayac = 0; for( float i = 0; i <= getHeight(); i += getHeight() * 1.0 / 100 ) { if( sayac % 10 != 0 ) { // Kısa çizgileri çizmeye yarar g2D.draw( new Line2D.Double( 0, getHeight() - i, getWidth() * 0.07, getHeight() - i ) ); } else { // Uzun çizgileri çizmeye ve yanlarına değerlerini yazmaya yarar // Birkaç parçaya ayırdım çünkü referans çizgilerin düzgün gözükmesi için // öyle gerekti... if( sayac == 0 ) { // En alttaki uzun çizgideysek g2D.draw( new Line2D.Double( 0, getHeight() - 1, getWidth() * 0.15, getHeight() - 1 ) ); g2D.drawString( "" + Termometre.altSinir, ( int )( getWidth() * 0.18 ), getHeight() - 2 ); } else if( sayac == 100 ) { // En üstteki uzun çizgideysek g2D.draw( new Line2D.Double( 0, 0, getWidth() * 0.15, 0 ) ); g2D.drawString( "" + Termometre.ustSinir, ( int )( getWidth() * 0.18 ), 10 ); } else { // Aralardaki bir uzun çizgideysek g2D.draw( new Line2D.Double( 0, getHeight() - i, getWidth() * 0.15, getHeight() - i ) ); g2D.drawString( "" + ( Termometre.altSinir + (int)( ( Termometre.ustSinir - Termometre.altSinir ) * sayac * 1.0 / 100 ) ), ( int )( getWidth() * 0.18 ), getHeight() - i + 5 ); } } sayac++; } // Aşağıdaki if ifadesi ender rastladığım bir bug'u çözmek için orada. // double değerlerle uğraşırken bazen >= ifadesi çok ufak küsüratlarla yanlış sonuç // döndürebiliyor ve bunun neticesinde üstteki for ifadesi i = getHeight()'ta değil de ondan // hafif önce bitiyor ve en üstteki referans çizgisi gözükmüyordu. Bu if() ifadesi ile onu // düzelttim. İsterseniz commentleyip sorunu görebilirsiniz... if( sayac == 100 ) { g2D.draw( new Line2D.Double( 0, 0, getWidth() * 0.15, 0 ) ); g2D.drawString( "" + Termometre.ustSinir, ( int )( getWidth() * 0.18 ), 10 ); } // REFERANS ÇİZGİLERİ ÇİZİM BİTİŞ // Referans çizgilerin sağına derece barını çiz (termometrenin grafiksel halini çiz) Color c = g.getColor(); // Derecenin üst veya alt sıcaklık sınırını aşması durumunda sorun yaşanmaması için tedbir int dereceTemp = derece(); if( dereceTemp < Termometre.altSinir ) dereceTemp = Termometre.altSinir; if( dereceTemp > Termometre.ustSinir ) dereceTemp = Termometre.ustSinir; // Termometrenin arkaplanını kırmızı renkte boya g.setColor( Color.RED ); g.fillRect( (int)( getWidth() * 0.55 ), 0, (int)( getWidth() * 0.25 ), getHeight() ); // Termometrenin değerini gösteren sıvıyı yeşil renkte boya g.setColor( Color.GREEN ); g.fillRect( (int)( getWidth() * 0.55 ), getHeight() - (int)( ( dereceTemp - Termometre.altSinir ) * 1.0 / ( Termometre.ustSinir - Termometre.altSinir ) * getHeight() ), (int)( getWidth() * 0.25 ), getHeight() ); // Termometrenin dış hatlarını siyah renkle vurgula g.setColor( Color.BLACK ); g.drawRect( (int)( getWidth() * 0.55 ), 0, (int)( getWidth() * 0.25 ), getHeight() - 1 ); g.setColor( c ); } public void mousePressed( MouseEvent e ) { // Termometreye mouse ile tıklandığında tüm termometrelerin sıcaklık değerlerini güncelle double oran = 1 - e.getY() * 1.0 / getHeight(); dereceBelirle( (int)( Math.round( oran * ( ustSinir - altSinir ) + altSinir ) ) ); uygulama.guncelle( t ); } public void mouseDragged( MouseEvent e ) { // Termometrede mouse tıklı halde sürüklendiğinde de tüm termometrelin // sıcaklık değerlerini güncelle // NOT: mouseDragged() metodunda nispeten yoğun bir işlem yapıldığından güçsüz // bilgisayarlarda bu metod kasabilir, içeriği commentlendiğinde sorun düzelir... double oran = 1 - e.getY() * 1.0 / getHeight(); dereceBelirle( (int)( Math.round( oran * ( ustSinir - altSinir ) + altSinir ) ) ); uygulama.guncelle( t ); } public void mouseClicked( MouseEvent e ) {} public void mouseEntered( MouseEvent e ) {} public void mouseExited( MouseEvent e ) {} public void mouseReleased( MouseEvent e ) {} public void mouseMoved( MouseEvent e ) {} } }
TermometreAyarlayici Class’ı
import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.SwingConstants; import javax.swing.JTextField; // TermometreAyarlayici - Kişiselleştirilebilir termometrenin değerlerini değiştirmeye ve // tüm termometrelerin gösterebileceği üst ve alt sıcaklık limitlerini belirlemeye yarayan class public class TermometreAyarlayici extends JPanel implements ActionListener { // DEĞİŞKENLER Termometre termometre; Uygulama u; JTextField kN, eN, ustSinir, altSinir; // CONSTRUCTOR public TermometreAyarlayici( Termometre t, Uygulama u ) { termometre = t; this.u = u; JLabel yazi1, yazi2, yazi3, yazi4; // Hangi TextField'ın hangi işe yaradığını açıklayan yardımcı yazılar // JLabel'da \n komutu çalışmadığı için yazi1 ve yazi2'yi 2 satıra ayırabilmek JLabel'larının // içinde biraz HTML kodu kullandım. (En pratik yolu buydu) yazi1 = new JLabel( "<html><body><center>Özel Termometre<br>Kaynama Noktası</center></body></html>" ); yazi2 = new JLabel( "<html><body><center>Özel Termometre<br>Erime Noktası</center></body></html>" ); yazi3 = new JLabel( "Üst Sıcaklık Sınırı" ); yazi4 = new JLabel( "Alt Sıcaklık Sınırı" ); // TextField'lar veri girebileceğimiz tek satırlık ufak alanlardır kN = new JTextField( 4 ); eN = new JTextField( 4 ); ustSinir = new JTextField( 5 ); altSinir = new JTextField( 5 ); // TextField'larda program ilk açıldığında bazı değerlerin yazmasını sağlıyorum // Aksi halde bu kutucuklar en başta boş olurdu kN.setText( "" + termometre.kN() ); eN.setText( "" + termometre.eN() ); ustSinir.setText( "" + Termometre.ustSinir ); altSinir.setText( "" + Termometre.altSinir ); // ActionListener ekliyorum veri girmeye yarayan kutucuklara ve Listener obje olarak bu class'ı // belirliyorum. ActionListener eklediğim zaman; kutucuğa birşeyler girdikten ve sonra Enter'a // bastıktan sonra Listener objenin actionPerformed( ActionEvent e ) metodu // otomatik olarak çağırılır. kN.addActionListener( this ); eN.addActionListener( this ); ustSinir.addActionListener( this ); altSinir.addActionListener( this ); // Yardımcı yazıları ortala yazi1.setHorizontalAlignment( SwingConstants.CENTER ); yazi2.setHorizontalAlignment( SwingConstants.CENTER ); yazi3.setHorizontalAlignment( SwingConstants.CENTER ); yazi4.setHorizontalAlignment( SwingConstants.CENTER ); // Paneli eşit ebatlardaki 4 satıra ve 1 sütuna ayır setLayout( new GridLayout( 4, 1 ) ); // Her bir parça için ayrı bir panel oluştur (her panel bir Label ve bir TextField barındıracak) JPanel parca1, parca2, parca3, parca4; parca1 = new JPanel(); parca2 = new JPanel(); parca3 = new JPanel(); parca4 = new JPanel(); parca1.add( yazi1 ); parca1.add( kN ); parca2.add( yazi2 ); parca2.add( eN ); parca3.add( yazi3 ); parca3.add( ustSinir ); parca4.add( yazi4 ); parca4.add( altSinir ); // Her bir parçayı sırayla ana panele ekle add( parca1 ); add( parca2 ); add( parca3 ); add( parca4 ); } // METODLAR // TextField'a veri girildiğinde ve Enter'a basıldığında çağırılır public void actionPerformed( ActionEvent e ) { if( e.getSource() == ustSinir ) { // Eğer termometrelerin üst sıcaklık sınırı değiştirilmeye çalışılmışsa... try { if( Integer.parseInt( ustSinir.getText() ) <= Integer.parseInt( altSinir.getText() ) ) throw new Exception(); // Eğer üst sınır alt sınırdan küçükse "catch" kısmına geç // Veri okunabilir ise (harf veya küsürat bulundurmuyorsa) değişikliği kabul et Termometre.ustSinir = Integer.parseInt( ustSinir.getText() ); } catch( Exception ex ) { // Hata durumunda TextField'ın eski değerine geri dön ustSinir.setText( "" + Termometre.ustSinir ); // Kısık bir "bip" sesi çıkarmaya yarar Toolkit.getDefaultToolkit().beep(); } } else if( e.getSource() == altSinir ) { // Eğer termometrelerin alt sıcaklık sınırı değiştirilmeye çalışılmışsa... try { if( Integer.parseInt( ustSinir.getText() ) <= Integer.parseInt( altSinir.getText() ) ) throw new Exception(); // Eğer üst sınır alt sınırdan küçükse "catch" kısmına geç // Veri okunabilir ise (harf veya küsürat bulundurmuyorsa) değişikliği kabul et Termometre.altSinir = Integer.parseInt( altSinir.getText() ); } catch( Exception ex ) { // Hata durumunda TextField'ın eski değerine geri dön altSinir.setText( "" + Termometre.altSinir ); // Kısık bir "bip" sesi çıkarmaya yarar Toolkit.getDefaultToolkit().beep(); } } else if( e.getSource() == eN ) { // Eğer özel termometrenin erime noktası değiştirilmeye çalışılmışsa... try { if( Integer.parseInt( eN.getText() ) >= Integer.parseInt( kN.getText() ) ) throw new Exception(); // Eğer erime noktası kaynama noktasından büyükse "catch" kısmına geç // Veri okunabilir ise (harf veya küsürat bulundurmuyorsa) değişikliği kabul et termometre.eNBelirle( Integer.parseInt( eN.getText() ) ); } catch( Exception ex ) { // Hata durumunda TextField'ın eski değerine geri dön eN.setText( "" + termometre.eN() ); // Kısık bir "bip" sesi çıkarmaya yarar Toolkit.getDefaultToolkit().beep(); } } else { // Eğer özel termometrenin kaynama noktası değiştirilmeye çalışılmışsa... try { if( Integer.parseInt( eN.getText() ) >= Integer.parseInt( kN.getText() ) ) throw new Exception(); // Eğer erime noktası kaynama noktasından büyükse "catch" kısmına geç // Veri okunabilir ise (harf veya küsürat bulundurmuyorsa) değişikliği kabul et termometre.kNBelirle( Integer.parseInt( kN.getText() ) ); } catch( Exception ex ) { // Hata durumunda TextField'ın eski değerine geri dön kN.setText( "" + termometre.kN() ); // Kısık bir "bip" sesi çıkarmaya yarar Toolkit.getDefaultToolkit().beep(); } } // Tüm ekranı güncelle repaint(); u.guncelle(); } }
Uygulama Class’ı
import java.awt.GridLayout; import javax.swing.JFrame; import javax.swing.JMenuBar; // Uygulama - main() metodunu kapsar ve ayrıca termometreleri depolar public class Uygulama { // DEĞİŞKENLER Termometre celcius, fahrenheit, kelvin, ozel; // CONSTRUCTOR public Uygulama() { // 4 adet termometre oluştur celcius = new Termometre( 0, 100, "Celcius" ); fahrenheit = new Termometre( 32, 212, "Fahrenheit" ); kelvin = new Termometre( 273, 373, "Kelvin" ); ozel = new Termometre( 400, 675, "Özel" ); celcius.uygulama = this; fahrenheit.uygulama = this; kelvin.uygulama = this; ozel.uygulama = this; // En başta termometrelerin sıcaklığını EN'si 0, KN'si 100 olan bir termometrede // 25 derece gösterecek şekilde ayarla ( bir başka deyişle 25 santigrat derece yap ) celcius.sicaklikAyarla( 25, 0, 100 ); fahrenheit.sicaklikAyarla( 25, 0, 100 ); kelvin.sicaklikAyarla( 25, 0, 100 ); ozel.sicaklikAyarla( 25, 0, 100 ); } // METODLAR // Tüm termometrelerin değerlerini, referans olarak verilen "t" termometresine bakarak güncelle public void guncelle( Termometre t ) { celcius.sicaklikAyarla( t.derece(), t.eN(), t.kN() ); fahrenheit.sicaklikAyarla( t.derece(), t.eN(), t.kN() ); kelvin.sicaklikAyarla( t.derece(), t.eN(), t.kN() ); ozel.sicaklikAyarla( t.derece(), t.eN(), t.kN() ); // Termometreleri yeni değerlerini gösterecek şekilde güncelle celcius.repaint(); fahrenheit.repaint(); kelvin.repaint(); ozel.repaint(); } // Tüm termometreleri yeniden çizdir, yani ekranı güncelle public void guncelle() { celcius.sicaklikAyarla( celcius.derece(), 0, 100 ); fahrenheit.sicaklikAyarla( celcius.derece(), 0, 100 ); kelvin.sicaklikAyarla( celcius.derece(), 0, 100 ); ozel.sicaklikAyarla( celcius.derece(), 0, 100 ); // Termometreleri güncelle celcius.repaint(); fahrenheit.repaint(); kelvin.repaint(); ozel.repaint(); } public static void main( String[] args ) { // DEĞİŞKENLER JFrame pencere; Uygulama u; // PROGRAM KODU u = new Uygulama(); // Ekranda 100,100 koordinatlarında bir JFrame oluştur pencere = new JFrame( "Termometre" ); pencere.setBounds( 100, 100, 650, 500 ); // "X" işaretine tıklayınca JFrame'in kapanmasını sağla pencere.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); // Pencereyi yatay düzlemde 5 parçaya ayır ( 1 satır, 5 sütuna ayır ) pencere.setLayout( new GridLayout( 1, 5 ) ); // JFrame'in içine 4 Termometre objesi ve bir de bazı değerleri değiştirmeye yarayan bir // Termometre Ayarlayıcısı ekle pencere.add( u.celcius ); pencere.add( u.fahrenheit ); pencere.add( u.kelvin ); pencere.add( u.ozel ); pencere.add( new TermometreAyarlayici( u.ozel, u ) ); // JFrame'in tepesine kişiselleştirilmiş bir menü bar ekle pencere.setJMenuBar( new MenuBar() ); // JFrame'i görünür yap (aksi halde program çalıştırılınca hiçbir şey gerçekleşmez) pencere.setVisible( true ); } }
MenuBar Class’ı
import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; // MenuBar - Pencerenin yukarısındaki menü barı için class public class MenuBar extends JMenuBar { public MenuBar() { JMenu dosya; JMenu bilgi; // Bunlar menü barında yer alan ana butonlar dosya = new JMenu( "Dosya" ); bilgi = new JMenu( "Bilgi" ); // Bunlar ise ana butonlara tıklayınca çıkacak olan, aksi halde gözükmeyen butonlar // "ara buton" diyelim bunlara JMenuItem cikis = new JMenuItem( new AbstractAction( "Çıkış" ) { public void actionPerformed(ActionEvent e) { System.exit( 0 ); } } ); JMenuItem hakkinda = new JMenuItem( new AbstractAction( "Hakkında" ) { public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog( null, "Termometre" + (char)(169) + "\n\nhttps://yasirkula.com/", "Hakkında", JOptionPane.INFORMATION_MESSAGE ); } } ); // "ara buton"ları ilgili ana butonlara ekliyorum dosya.add( cikis ); bilgi.add( hakkinda ); // ana butonları menü barın kendisine ekliyorum // NOT: Dilersem menü bara direkt ara buton da ekleyebilirim ve çalışır da. İsterseniz deneyin... add( dosya ); add( bilgi ); } }
Umarım işinize yarar. Başka örneklerde görüşmek üzere!