10. [TD]: [ui] katmanının bir Swing arayüzü ile uygulanması
Anahtar kelimeler: çok katmanlı mimari, Spring, bağımlılık enjeksiyonu, Swing bileşen kütüphanesi.
![]() |
10.1. Support
![]() |
[UI] katmanında bir Swing grafik arayüzü oluşturmak istiyoruz. NetBeans, bu Swing arayüzlerini oluşturmak için Eclipse'in sunduklarından daha üstün bir [Matisse] aracına sahiptir. Swing arayüzleri, giderek JavaFx arayüzleriyle yer değiştirme eğilimindedir. NetBeans ve Eclipse, bu arayüzleri oluşturmak için aynı aracı kullanır. Dolayısıyla, JavaFx arayüzleri oluşturursak, katmanlı mimarinin başından sonuna kadar Eclipse'i kullanmaya devam edebiliriz.
NetBeans, herhangi bir Maven projesini açabilir. Bu nedenle, önceki Maven projesini kullanıp ona bir Swing arayüzü ekleyeceğiz. [2]'te, Eclipse ile oluşturduğumuz üç katmanın Maven projelerini (Dosya / Projeyi Aç) yükleriz. Ardından, bunların [3] ikili dosyalarını derleriz. [Build] ve [Clean and Build] seçenekleri, uygulandıkları projenin ikili dosyasını oluşturur. Bu ikili dosyalar, projenin [target] [4-5] klasörüne yerleştirilir:
![]() |
[Clean] seçeneği, bu [target] klasörünü siler. [Build] seçeneği ise bu klasörü yeniden oluşturur. Deneyimler, beklenmedik sorunlarla karşılaşıldığında ilk yapılması gerekenin, projenin en son sürümüyle çalıştığınızdan emin olmak için proje üzerinde bir [Clean and Build] komutu çalıştırmak olduğunu göstermektedir. Bu, özellikle değiştirildiklerinde proje çalıştırıldığında otomatik yeniden derlemeyi tetiklemeyen yapılandırma dosyaları olduğunda gereklidir. Bu durumda, yeni sürümlerin [target] klasörüne yüklenmesi için [Clean and Build] komutuyla yeniden derlemeyi zorlamak gerekir.
10.2. Uygulamanın Çalışması
[Elections] uygulamasının genel mimarisine geri dönelim:
![]() |
Şimdi [ui] katmanının yeni bir uygulamasına odaklanacağız. Şu ana kadar gerçekleştirilen tek uygulama bir konsol arayüzüdür. Şimdi bir grafik kullanıcı arayüzü oluşturacağız.
Kullanıcı, [Elections] uygulamasıyla etkileşim kurmak için aşağıdaki arayüzü kullanacak:
![]() |
![]() |
Grafik kullanıcı arayüzü, [ui] katmanında yer almaktadır. Kullanıcıyla etkileşime giren bu arayüzdür.
- Başlangıçta, [main] konsol uygulaması, Spring aracılığıyla uygulamanın üç katmanını başlatır. Bu işlem, grafik arayüz görünür hale gelmeden önce gerçekleştirilir. Yine bu başlatma aşamasında, seçimi tanımlayan bilgiler (doldurulacak koltuk sayısı, seçim barajı, yarışan listeler) [dao] katmanından istenir. Bu başlatma aşaması başarısız olursa (örneğin verilere erişilememesi gibi), konsolda bir hata mesajı görüntülenir ve grafik arayüz görüntülenmez.
- Verilerin okunması başarılı olursa, grafik arayüz aşağıdaki bilgilerle birlikte görüntülenir (yukarıdaki ekran görüntüsüne bakınız):
- (2) numaralı koltukta doldurulacak koltuk sayısı
- (3)’teki seçim barajı
- (4) numaralı kısımda aday listelerin kimlik numaraları ve isimleri
- kullanıcı daha sonra 4 (kimlik - ad), 5 (oy) ve 6 (ekle) alanlarını kullanarak her aday listesine oy sayısını atar.

- ardından (10) numaralı bağlantıyı kullanarak sandalye sayısını hesaplayabiliriz:

- [Enregistrer] (12) bağlantısı, sonuçları veri kaynağına kaydetmeye olanak tanır.
10.3. [ui] katmanının uygulaması olan [ElectionsSwing] sınıfı
10.3.1. NetBeans projesi
Not: 22.4 numaralı paragrafta NetBeans'i nasıl edinebileceğiniz açıklanmaktadır.
Uygulamanın nihai NetBeans projesi [1] şeklinde olacaktır. [2-5] adımlarını izleyerek bunu derleyin:
![]() |
![]() |
Projenin, JDK 1.8 [1-6] ile derlenecek şekilde yapılandırıldığından emin olun:
![]() |
10.3.2. Maven Yapılandırması
Yeni [elections-swing-metier-dao-jdbc] projesi, önceki [elections-console-metier-dao-jdbc] projesini temel alacaktır. Bunun için aşağıdaki şekilde bir Maven bağımlılığı eklenir: [1-3]:
![]() |
![]() |
10.3.3. Grafik kullanıcı arayüzünün oluşturulması
Grafik kullanıcı arayüzünü oluşturmak için şu adımları uygulayabiliriz:
![]() |
![]() |
- [1]: [elections.ui.service] paketine bir nesne eklenmesi
- [2]: [Swing GUI Forms] kategorisinde [JFrame Form] seçeneğinin seçilmesi
![]() |
- [4]: sınıfa bir ad verilmesi
- [5]: Sınıfın paketi.
- Sihirbazı tamamlayın.
- [6]: Oluşturulan sınıf
![]() |
- [7]: [AbstractElectionsSwing] sınıfı, [Design] modunda
- [8]: penceredeki bileşenlerin [9] ağacını gösteren [Navigator] sekmesi
- [10]: [9]'te seçilen [jFrame] bileşeninin özelliklerini görüntüleyen [Properties] sekmesi
![]() |
- [11]: [JFrame] bir bileşen konteyneridir. Bu bileşenler, layouts olarak adlandırılan çeşitli konumlandırma kurallarına göre konteyner içinde düzenlenebilir. Burada, bileşenlerin konteyner içinde serbestçe konumlandırılmasını sağlayan layout, [Free Design] ve [14] seçeneklerini seçiyoruz.
Bileşenleri, Palette adlı araç çubuğunda bulabiliriz:
![]() |
- [1]: palet
- [2]: bir bileşen JLabel bileşen konteynerine yerleştirilir
- üzerine sağ tıklayarak çeşitli özelliklere erişilebilir: adı [4], metni [3] veya olay işleyicileri [5]. [3]'i kullanarak [6] metnini sabitliyoruz.
![]() |
- [1]: [JLabel] bileşeninin [Properties] sekmesi, bu bileşenin özelliklerine erişim sağlar: yatay konumlandırması [2], dikey konumu [3], metin yazı tipi [4], metin [5].
Bir bileşen grafik arayüzüne yerleştirilip yapılandırıldığında ve yapılan çalışma kaydedildiğinde (Ctrl-S), [AbstractElectionsSwing] sınıfında kod oluşturulur:
![]() |
![]() |
Bu gri renkli kodu değiştirmemelisiniz, çünkü bir sonraki kaydetme işleminde silinip yeniden oluşturulur. Yapılan değişiklikler bu durumda kaybolur.
NetBeans ile bir grafik kullanıcı arayüzü oluşturmaya ilişkin bir eğitim kılavuzu, URL ve [https://netbeans.org/kb/docs/java/quickstart-gui.html?print=yes#design] (Kasım 2015) dosyalarında bulunabilir.
Şimdi şu arayüzü oluşturacağız:
![]() |
Arayüzün bileşenleri şunlardır:
No | tür | ad | rol |
JMenuBar | jMenuBar1 | bir menü | |
JLabel | jLabelSAP | doldurulacak koltuk sayısı | |
JLabel | jLabelSE | seçim barajı | |
JComboBox | jComboBoxNomsListes | yarışan listelerin isim listesi | |
JTextField | jTextFieldVoixListe | bir listenin oy sayısı | |
JLabel | jLabelAjouter | (8) numaralı listeye bir liste eklemek için | |
(JScrollPane, JList) | jListNomsVoix | listelerin adları ve sesleri | |
JLabel | jLabelSupprimer | (8) numaralı adımda seçilen listeyi (8) numaralı adımdan silmek için | |
JLabel | jLabelCalculer | seçim sonuçlarını hesaplamak için | |
JLabel | jLabelEffacer | seçim sonuçlarını silmek için | |
JLabel | jLabelEnregistrer | seçim sonuçlarını kaydetmek için | |
(JScrollPane, JList) | jListResultats | seçim sonuçlarını görüntülemek için | |
(JScrollPane, JTextPane) | jTextPaneMessages | takip mesajlarını görüntülemek için |
(JScrollPane, JList) [13-14], [JList] bileşenini pencereye sürükleyip bıraktığınızda, bu bileşenin listeyi kaydırmaya olanak tanıyan [JScrollPane] bileşenine otomatik olarak ekleneceğini belirtmek için kullanılmaktadır. [JScrollPane] bileşeni, herhangi bir anda yalnızca sınırlı sayıda öğe görünürken listenin tüm öğelerini görüntülemeyi sağlar. Aynı durum [JTextPane] ve [15-16] bileşenleri için de geçerlidir.
Menü şu şekilde oluşturulabilir:
![]() |
- [1, 2]: [Menu Bar] bileşeni pencereye yerleştirilir
- [3]: [Navigator] sekmesinde gösterildiği gibi varsayılan olarak oluşturulan menü
- [4,5,6]: Bir menü seçeneğine sağ tıklayarak şunları yapabilirsiniz:
- metnini değiştirebilir ([4]), adını değiştirebilir ([5])
- olaylarını yönetmek [6]
- [7]: istenen menü
İstenen menü şöyledir:
seviye 1 | 2. seviye |
Seçimler | |
Quitter | |
Listeler | |
Ajouter | |
Supprimer | |
Sonuçlar | |
Calculer | |
Effacer | |
Enregistrer | |
Hakkında |
Grafik arayüzü istediğiniz zaman test edebilirsiniz:
![]() |
Arayüzü oluştururken, belirli etiketlere ve menülere bir olay işleyicisi atamanız gerekir: [Ajouter, Effacer, ...]. Bunun nasıl yapılacağı aşağıda açıklanmıştır:
![]() |
- [1]: Olayını yönetmek istediğiniz bileşene sağ tıklayın
- [2]: [Events] seçeneğini seçin
- [3]: Bir olay kategorisi seçin
- [4]: Yönetmek istediğiniz olayı seçin
Bu işlemle oluşturulan Java kodu şöyledir:
jLabelCalculer.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jLabelCalculerMouseClicked(evt);
}
});
...
private void jLabelCalculerMouseClicked(java.awt.event.MouseEvent evt) {
// TODO işleme kodunuzu buraya ekleyin:
}
- 1-5. satırlar: jLabelCalculer bileşenine bir olay işleyicisi eklenir. addMouseListener yöntemi, aşağıdaki MouseListener arayüzünü uygulayan bir sınıfı parametre olarak bekler:
![]() |
MouseListener arayüzü, MouseAdapter sınıfı da dahil olmak üzere çeşitli sınıflar tarafından uygulanmaktadır. Bu sınıf, MouseListener arayüzünün beş yöntemini uygular, ancak bu yöntemler hiçbir işlem yapmaz. Bu nedenle, istenen yöntemi veya yöntemleri uygulamak için bu sınıftan türetme yapmak gerekir. Bu işlem, yukarıdaki kodda gerçekleştirilmiş ve aşağıda tekrarlanmıştır:
jLabelCalculer.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jLabelCalculerMouseClicked(evt);
}
});
Yukarıdaki kod, [ref1] dersinin 2.5. paragrafında açıklanan anonim sınıf tekniğini kullanmaktadır.
-
satırda, addMouseListener yönteminin parametresi, anında tanımlanan anonim bir sınıftır. Bu, MouseAdapter sınıfından türetilmiş bir sınıfın örneğidir (1. satır) ve mouseClicked yöntemini (2-4. satırlar) bir işlem yapması için yeniden tanımlamaktadır.
-
satır olarak adlandırılan jLabelCalculerMouseClicked yöntemi şu şekilde tanımlanmıştır:
private void jLabelCalculerMouseClicked(java.awt.event.MouseEvent evt) {
// TODO işleme kodunuzu buraya ekleyin:
}
Geliştirici, bu yönteme kod ekleyerek "MouseClicked" olayını yönetir.
Tüm olay işleyicileri NetBeans tarafından bu şekilde oluşturulur. Geliştirici, bir bileşenin olayına bir yöntem atamak için NetBeans tarafından oluşturulan kod satırlarını göz ardı edebilir. Yalnızca yukarıdaki 2. satıra kendi kodunu yazmakla yetinebilir. İşte bir örnek:
private void jLabelCalculerMouseClicked(java.awt.event.MouseEvent evt) {
System.out.println("Mouse Clicked");
}
Grafik kullanıcı arayüzünü çalıştırıp [Calculer] bağlantısına tıklandığında konsolda şu mesaj görüntülenir:
![]() |
- [1]: [Calculer] etiketine iki kez tıklanır
- [2]: Bu olayın işleyicisi çalıştırıldı ve NetBeans konsolunda mouseClicked mesajlarını üretti.
[jComboBoxNomsListes, jListNomsVoix, jListResultats] bileşenleri şu şekilde tanımlanmıştır:
protected javax.swing.JComboBox jComboBoxNomsListes;
protected javax.swing.JList jListNomsVoix;
protected javax.swing.JList jListResultats;
Bu bileşenler, normalde T türüyle parametrelendirilen listelerdir: T türü, bileşenler tarafından görüntülenen modelin öğelerinin türüdür. Bu T türü herhangi bir tür olabilir. Liste bileşeninde görüntülenen değer, [String] türündedir. Varsayılan olarak, görüntüleme için [T.toString()] yöntemi kullanılır. Görüntülenecek içeriği daha iyi kontrol edebilmek için, burada T türü String türü olacaktır. Dolayısıyla, listelerimizin doğru tanımlaması şu şekildedir:
protected javax.swing.JComboBox<String> jComboBoxNomsListes;
protected javax.swing.JList<String> jListNomsVoix;
protected javax.swing.JList<String> jListResultats;
Bu sonuca, bileşenin özelliklerinden birini değiştirerek ulaşılır:
![]() |
10.3.4. Kodun ayrılması
Uygulamamızın yapısına geri dönelim:
![]() |
[AbstractElectionsSwing] sınıfı, yukarıdaki [ui] katmanını uygulamalıdır. NetBeans tarafından oluşturulan kodu, şimdilik sadece pencere yönetimi kodunu ve şu anda hiçbir işlev görmeyen olay işleyicilerini içermektedir. Yukarıda görüldüğü gibi, [AbstractElectionsSwing] sınıfı, [métier] katmanıyla olan etkileşimleri yönetmelidir. Bu yönetim, olay işleyicilerinde gerçekleştirilecektir. Kod yapısını netleştirmek için, kodu iki sınıfa ayırmaya karar veriyoruz:
- [AbstractElectionsSwing], NetBeans tarafından oluşturulduğu haliyle, birkaç küçük ayrıntı dışında kalacaktır. Bu sınıfın kendisi hiçbir olayı yönetmeyecektir. Olay işleyicileri boş olacak ve soyut olarak tanımlanacaktır. Bunlar, [AbstractElectionsSwing] sınıfından türetilen bir sınıf tarafından uygulanacaktır.
- [ElectionsSwing], [AbstractElectionsSwing]'ten türetilen ve tüm olay işleyicilerini uygulayacak olan sınıftır.
Bu tür bir ayrım alışılmadık bir durum değildir. Örneğin, ASP.NET web sayfalarında (MVC sürümü hariç) bu ayrım görülmektedir. NetBeans projesi şu şekilde gelişmektedir:
![]() |
[AbstractElectionsSwing] sınıfının kodu ise şu şekilde gelişmektedir:
public abstract class AbstractElectionsSwing {
....
private void jMenuItemCalculerActionPerformed(java.awt.event.ActionEvent evt) {
doCalculer();
}
...
private void jLabelCalculerMouseClicked(java.awt.event.MouseEvent evt) {
if (jLabelCalculer.isEnabled()) {
doCalculer();
}
}
....
// olay işleyicileri
abstract protected void doSupprimer();
abstract protected void doCalculer();
abstract protected void doQuitter();
abstract protected void doEffacer();
abstract protected void doEnregistrer();
abstract protected void doAjouter();
abstract protected void doInformer();
abstract protected void doMajLabelAjouter();
abstract protected void doMajLabelSupprimer();
...
}
- 1. satır: sınıf, soyut olarak tanımlanmıştır
- 3-5. satırlar: [jMenuItemCalculer] menü seçeneğine yapılan tıklamanın işlenmesi. Görüldüğü gibi, olayın işlenmesi 19. satırdaki doCalculer yöntemine aktarılmıştır. Bu yöntem uygulanmamıştır ve soyut olarak tanımlanmıştır. Bu yöntemi, türetilmiş sınıf olan [ElectionsSwing] uygulayacaktır;
- 9-13. satırlar: [jLabelCalculer] etiketindeki clic olay işleyicisi. Tıklama, [jLabel] bileşeni ister etkin (enabled=true) ister devre dışı (enabled=false) olsun, her zaman bir olayı tetikler. Burada, olayı işleyebilmek için bileşenin etkin olduğundan emin olunur;
- 15. satır ve sonrası: Olay işlemeyi soyut bir yönteme devretme tekniği, tüm olay işleyicilere uygulanır.
[AbstractElectionsSwing] sınıfından türetilen [ElectionsSwing] sınıfı, [AbstractElectionsSwing] tarafından uygulanmayan tüm olay işleyicilerini uygular:
package elections.ui.service;;
...
public class ElectionsSwing extends AbstractElectionsSwing {
// olay işleyicileri
@Override
protected void doInformer() {
...
}
@Override
protected void doAjouter() {
...
}
@Override
protected void doCalculer() {
...
}
@Override
protected void doEffacer() {
...
}
@Override
protected void doEnregistrer() {
...
}
@Override
protected void doQuitter() {
System.exit(0);
}
@Override
protected void doSupprimer() {
...
}
@Override
protected void doMajLabelAjouter() {
...
}
@Override
protected void doMajLabelSupprimer() {
...
}
}
- 3. satır: [ElectionsSwing], [AbstractElectionsSwing]'ten türetilmiştir
- 7-50. satırlar: Grafik penceresinin olay işleyicileri
Türetilmiş sınıf [ElectionsSwing]'in yöntemleri, üst sınıf [AbstractElectionsSwing]'in bileşenlerini işleyecektir. Şu anda bu bileşenlerin kapsamı private olduğundan, alt sınıf olan [ElectionsSwing]'in bunlara erişimi engellenmiştir:
private JMenuItem jMenuItemAPropos = null;
private JLabel jLabelAjouter = null;
Bu sorunu çözmek için, grafik arayüz bileşenlerinin kapsamını [protected] olarak ayarlayacağız:
![]() |
- [3] değerine, [protected] özniteliğini ayarlayın;
10.3.5. [IElectionsUI] arayüzünün uygulanması
Uygulamamızın yapısına geri dönelim:
![]() |
Yukarıda, [ui] katmanı, [IElectionsUI] arayüzünü [main] nesnesine sunmalıdır:
package elections.ui.service;
public interface IElectionsUI {
/**
* lance le dialogue avec l'utilisateur
*/
public void run();
}
Bu arayüz, [elections-console-metier-dao-jdbc] projesinde tanımlanmış ve 9.4. paragrafında açıklanmıştır. Bu proje, [swing] projesinin bir bağımlılığı olduğundan, bu arayüz bilinmektedir.
[AbstractElectionsSwing] sınıfı soyut hale geldiği için artık Spring tarafından örneklenemez. Artık [ElectionsSwing] sınıfı örneklenmelidir. [ElectionsSwing] sınıfı, [IElectionsUI] arayüzünü uygulamalıdır. Dolayısıyla kodunda şu değişiklik yapılır:
public class ElectionsSwing extends AbstractElectionsSwing implements IElectionsUI {
// [ElectionsUI] arayüzünün run yöntemi
public void run() {
...
}
- 1. satır: [ElectionsSwing] sınıfı, [IElectionsUI] arayüzünü uygular
- 4-6. satırlar: Bu arayüzün [run] yöntemi
run yöntemi ne yapmalıdır? Grafik penceresini görüntülemelidir. Bunu nasıl yapabiliriz? NetBeans'in [AbstractElectionsSwing] sınıfında oluşturduğu ve istenen işlevi yerine getiren [main] yöntemini örnek alabiliriz:
public static void main(String args[]) {
/* Nimbus görünüm ve his ayarlarını belirle */
//<editor-fold defaultstate="collapsed" desc=" Görünüm ve his ayar kodu (isteğe bağlı) ">
/* Nimbus (Java SE 6 sürümünde tanıtılmıştır) kullanılamıyorsa, varsayılan görünüm ve his ile devam edin.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Formu oluşturun ve görüntüleyin */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
- satırda kullanılan [AbstractElectionsSwing] oluşturucu şöyledir:
public AbstractElectionsSwing() {
initComponents();
}
- 2. satır: [initComponents] yöntemi, grafik arayüz oluşturucu tarafından oluşturulan özel bir yöntemdir. Bu yöntemin kodu değiştirilemez.
[ElectionsSwing] sınıfındaki [run] yöntemi ise şu şekilde olabilir:
@Override
public void run() {
// grafik kullanıcı arayüzünü görüntüleme
SwingUtilities.invokeLater(new Runnable() {
public void run() {
init();
setVisible(true);
}
});
}
- 6. satır: Grafik arayüzü, [init] yöntemi kullanılarak başlatılır. Burada, üst sınıftaki [initComponents] yöntemini çağırmak istiyoruz, ancak bu yöntem özel olarak tanımlanmıştır. Bu durumda, üst sınıf [AbstractElectionsSwing]’e aşağıdaki [init] yöntemi eklenir:
protected void init(){
initComponents();
}
- (devam)
- [AbstractElectionsSwing] sınıfında bulunduğu için, [init] yöntemi aynı sınıftaki özel [initComponents] yöntemine erişebilir;
- [protected] özniteliğine sahip olduğu için, alt sınıf [ElectionsSwing]'te görülebilir;
- 7. satır: grafik arayüz görünür hale getirilir;
Not: [run] yöntemi [ElectionsSwing] sınıfına yazıldığında, [AbstractElectionsSwing] soyut sınıfındaki [main] yöntemi silinebilir.
10.3.6. Çalıştırılabilir sınıf
Uygulamamızın yapısına geri dönelim:
![]() |
Spring'in, [ui] katmanını, daha önce console uygulaması tarafından uygulandığı zaman olduğu gibi örneklendirmesini istiyoruz. Bunun için, [ElectionsSwing] uygulama sınıfının [métier] katmanına bir referansı olması gerekir:
@Component
public class ElectionsSwing extends AbstractElectionsSwing implements IElectionsUI{
// [métier] katmanı hakkında bilgi
@Autowired
private IElectionsMetier metier;
...
- 1. satır: [ElectionsSwing] sınıfı bir Spring bileşenidir;
- 5-6. satırlar: Spring tarafından [métier] katmanına bir referans enjeksiyonu;
Grafik kullanıcı arayüzü, aşağıdaki [BootElectionsSwing] sınıfının çalıştırılmasıyla başlatılır:
![]() |
package elections.ui.boot;
import elections.ui.service.IElectionsUI;
public class BootElectionsSwing extends AbstractBootElections {
public static void main(String[] arguments) {
new BootElectionsSwing().run();
}
@Override
protected IElectionsUI getUI() {
return ctx.getBean("electionsSwing", IElectionsUI.class);
}
}
Benzer bir kodu, 9.5. paragrafta [AbstractBootElections] ve [BootElectionsConsole] sınıflarının kodunu açıklarken ele almıştık. 12. satırda, [ElectionsSwing] sınıfının standart Spring adına karşılık gelen [electionsSwing] adlı bean'i alıyoruz.
10.3.7. Grafik kullanıcı arayüzünün başlatılması
Grafik arayüz görüntülendiğinde, bazı bileşenleri başlatılmış olur:

Yukarıda şunlar görülmektedir:
- kombinasyon kutusu, liste isimleriyle doldurulmuştur;
- doldurulacak koltuk sayısı ve seçim barajı belirtilmiştir;
- bazı bağlantıların devre dışı bırakılmış olduğu;
- pencerenin altında bir başarı mesajı görüntüleniyor;
Bu başlatma işlemleri ne zaman gerçekleşecek? Bu işlemler, ancak [ElectionsSwing] sınıfındaki [electionsMetier] alanının başlatılmasından sonra gerçekleştirilebilir. Zira liste isimleri, [metier] katmanından istenecektir. Bu alanın başlatılması, Spring tarafından aşağıdaki sırayla gerçekleştirilecektir:
- [ElectionsSwing] sınıfının parametresiz oluşturucusunun kullanılması;
- bağımlılıkların enjeksiyonu, bu durumda [métier] katmanının referansı;
- [ElectionsSwing] sınıfının [run] yönteminin yürütülmesi:
@Override
public void run() {
// grafik kullanıcı arayüzü görüntüleniyor
SwingUtilities.invokeLater(new Runnable() {
public void run() {
init();
setVisible(true);
}
});
}
- 12. satırda, grafik arayüzün bileşenlerini çizecek olan üst sınıfın [init] yöntemini çağırdığımızı belirtmiştik. Bu yöntemi, [ElectionsSwing] sınıfında yerel olarak yeniden tanımlayacağız. Pencerenin bileşenlerini (açılır menü, etiketler) bu yöntem içinde, bu sefer verilerle birlikte başlatacağız:
Yerel [init] yöntemi şu şekilde bir iskelete sahip olabilir:
public class ElectionsSwing extends AbstractElectionsSwing implements IElectionsUI {
...
// [métier] katmanına ilişkin referans
@Autowired
private IElectionsMetier metier;
// başlatma işlemleri
public void init() {
// üst sınıf tarafından bileşenlerin oluşturulması
super.init();
// [metier] katmanından listeler istenir
...
// liste adları, jComboBoxNomsListes
...
// ile seçim parametreleri
...
// bu iki bilgiyle ilişkili etiketler başlatılır
...
// başarı mesajı
...
// bazı form bileşenlerinin durumunun başlatılması
...
}
- satırda, üst sınıftaki [init] yönteminin çağrıldığına dikkat edelim.
10.3.8. [Utilitaires] sınıfı
[Utilitaires] sınıfında bir dizi statik yardımcı yöntem bir araya getirilmiştir:
![]() |
[Utilitaires] sınıfı şu şekildedir:
package istia.st.elections.ui;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
//yardımcı sınıf
class Utilitaires {
// etiket tablosunun durumunu yönetme
public static void setEnabled(JLabel[] labels, boolean value) {
for (int i = 0; i < labels.length; i++) {
labels[i].setEnabled(value);
}
}
// menü seçenekleri tablosunun durumunu yönetme
public static void setEnabled(JMenuItem[] menuItems, boolean value) {
...
}
}
- 9. satır: setEnabled yöntemi, bir dizide tanımlanan JLabel bileşenlerinin durumunu belirler. JLabel bileşeninin setEnabled yöntemi, JLabel'i etkinleştirmeye veya devre dışı bırakmaya olanak tanır.
Yapılması gereken iş: 9. satırdaki setEnabled yöntemini örnek alarak, 16. satırdaki setEnabled yöntemini yazın; bu yöntem, JMenuItem bileşenleri için aynı işlevi yerine getirir.
10.3.9. [ElectionsSwing] sınıfının kodu
[ElectionsSwing] sınıfının genel yapısını hatırlayalım:
package istia.st.elections.ui;
...
public class ElectionsSwing extends AbstractElectionsSwing implements IElectionsUI {
// [métier] katmanı hakkında bilgi
@Autowired
private IElectionsMetier metier;
// başlatma işlemleri
public void init() {
...
}
// olay işleyicileri
@Override
protected void doInformer() {
...
}
@Override
protected void doAjouter() {
...
}
@Override
protected void doCalculer() {
...
}
@Override
protected void doEffacer() {
...
}
@Override
protected void doEnregistrer() {
...
}
@Override
protected void doQuitter() {
System.exit(0);
}
@Override
protected void doSupprimer() {
...
}
@Override
protected void doMajLabelAjouter() {
...
}
@Override
protected void doMajLabelSupprimer() {
...
}
}
Sınıfın yöntemlerini sırayla inceleyeceğiz.
10.3.9.1. [init] yöntemi
Grafik arayüze geri dönelim:
![]() |
[init] yönteminin amaçları şunlardır:
- [4] kombinasyonunu, listelerin kimlik numaraları ve adlarıyla [id - nom] biçiminde doldurmak
- [15]'te bir başarı mesajı görüntülemek
- [2] ve [3] etiketlerini başlatmak
- bazı bağlantıları devre dışı bırakmak
[init] yönteminin iskeleti şu şekilde olabilir:
@Override
protected void init() {
// üst sınıf tarafından bileşenlerin oluşturulması
super.init();
// yerel başlatmalar
modèleNomsVoix = new DefaultListModel<>();
jListNomsVoix.setModel(modèleNomsVoix);
modèleRésultats = new DefaultListModel<>();
jListResultats.setModel(modèleRésultats);
String info;
try {
// [métier] katmanından listeler istenir
listes = ...
// liste adları jComboBoxNomsListes açılır menüsüne eşleştirilir
...
// ve seçim parametreleri
int nbSiegesAPourvoir = ...
double seuilElectoral = ...
// bu iki bilgiyle ilişkili etiketler başlatılır
...
// başarı mesajı
info = "Source de données lue avec succès";
} catch (ElectionsException ex1) {
// hata kaydedilir
info = getInfoForException("Les erreurs suivantes se sont produites :", ex1);
} catch (RuntimeException ex2) {
// hata kaydedilir
info = getInfoForException("Les erreurs suivantes se sont produites :", ex2);
}
// bilgi görüntülenir
jTextPaneMessages.setText(info);
jTextPaneMessages.setCaretPosition(0);
// form durumu
Utilitaires.setEnabled(new JLabel[] { jLabelAjouter, jLabelCalculer, jLabelEnregistrer, jLabelSupprimer }, false);
Utilitaires.setEnabled(
new JMenuItem[] { jMenuItemAjouter, jMenuItemCalculer, jMenuItemEnregistrer, jMenuItemSupprimer }, false);
// pencereyi ortala
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
}
private String getInfoForException(String message, ElectionsException ex) {
// mesaj görüntülenir
StringBuffer info = new StringBuffer(String.format("%s -------------\n", message));
info.append(String.format("Code erreur : %d\n", ex.getCode()));
// hatalar görüntülenir
for (String erreur : ex.getErreurs()) {
info.append(String.format("-- %s\n", erreur));
}
return info.toString();
}
private String getInfoForException(String message, RuntimeException ex) {
// mesajı göster
StringBuffer info = new StringBuffer(String.format("%s -------------\n", message));
// istisna yığını görüntülenir
Throwable cause = ex;
while (cause != null) {
info.append(String.format("-- %s\n", cause.getMessage()));
cause = cause.getCause();
}
return info.toString();
}
Yapılması gereken iş: [init] yönteminin kodunu tamamlayın.
Ders içinde okunacaklar: JTextField, JLabel bileşenleri
Bilmeniz gerekenler:
Bir JList bileşeni, bir şablonda bulunan verileri görüntüler. Varsayılan olarak, bu şablon DefaultListModel türündedir (2. ve 3. satırlar). DefaultListModel nesnesi, ArrayList türüyle biraz benzer şekilde çalışır:
- Şablona bir o nesnesi eklemek için:
[DefaultListModel].addElement(Object o);
Bu uygulamada, o nesnesi her zaman String türünde olacaktır.
- Şablondan i numaralı öğeyi kaldırmak için:
[DefaultListModel].remove(int i);
- Şablondaki i numaralı öğeyi almak için:
[DefaultListModel].elementAt(int i);
[jComboBoxNomsListes] kombinasyonuna bir öğe eklemek için [addItem] yöntemi kullanılır:
jComboBoxNomsListes.addItem(chaîne de caractères)
JTextPane bileşeni, görüntülenen metni okumak/yazmak için getText() ve setText() yöntemlerine sahiptir.
10.3.9.2. [Ajouter] bağlantısının durumunu yönetme
[Ajouter] [6] bağlantısı, yalnızca seslerin [5] alanı boş olmadığında etkindir. [AbstractElectionsSwing] sınıfında, [5] alanındaki imleç hareketlerini izleyen işleyici şöyledir:
private void jTextFieldVoixListeCaretUpdate(javax.swing.event.CaretEvent evt) {
doMajLabelAjouter()
}
- satır, [ElectionsSwing] sınıfının [doMajLabelAjouter] yöntemini çağırır.
protected void doMajLabelAjouter() {
// etiketin durumunu ayarlar [jLabelAjouter]
...
// [jMenuItemAjouter] menüsünün durumu belirlenir
...
}
Yapılması gereken iş: [doMajLabelAjouter] yönteminin kodunu tamamlayın.
10.3.9.3. Her listeye oyları atayın
(4)'teki her aday liste için şu şekilde ilerleyin:
- (4) numaralı listeden bir liste seçin
- (5) numaralı alana oy sayısını girin
- [Ajouter] bağlantısına tıklayarak onaylayın
Giriş hataları aşağıdaki örnekte gösterildiği gibi bildirilir:

Oy sayısı doğruysa, liste (8) bileşenine eklenir, oy sayısı silinir ve [Ajouter] bağlantısı kapanır:
![]() | ![]() |
[AbstractElectionsSwing] sınıfında, [Ajouter] bağlantısına yapılan tıklamayı yöneten işleyici şöyledir:
private void jLabelAjouterMouseClicked(java.awt.event.MouseEvent evt) {
if (jLabelAjouter.isEnabled()) {
doAjouter();
}
}
- satır, [ElectionsSwing] sınıfının [doAjouter] yöntemini çağırır:
// liste şablonları JList
private DefaultListModel<String> modèleNomsVoix = null;
private DefaultListModel<String> modèleRésultats = null;
// yarışmadaki listeler
private ListeElectorale[] listes;
// kullanıcı tarafından girilen listeler
private final List<ListeElectorale> listesSaisies = new ArrayList<>();
private ListeElectorale[] tListesSaisies;
...
@Override
protected void doAjouter() {
// oy sayısı doğru mu?
...
// hata varsa, bildirilir
if (erreur) {
JOptionPane.showMessageDialog(null, "Nombre de voix incorrect", "Elections : erreur",
JOptionPane.INFORMATION_MESSAGE);
jTextFieldVoixListe.requestFocus();
// grafik arayüze geri dön
return;
}
// hata yok - liste kaydedilir
listesSaisies.add(...);
modèleNomsVoix.addElement(...);
// oy sayısını sıfırlayalım
jTextFieldVoixListe.setText("");
// form durumu (menüler, etiketler)
...
}
- 25. satır: Kullanıcı bir listeye sesler ekleyip seçimini onayladığında, bu liste 9. satırdaki [listesSaisies] alanına yerleştirilir. Liste, [id, version, nom, voix] bilgileriyle birlikte buraya kaydedilir. İlk üç bilgi, başlangıçta 6. satırdaki tabloya kaydedilen listelerden alınır. Kombinasyon menüsündeki [getSelectedIndex] yöntemi, seçilen listenin indeksini öğrenmeyi sağlar;
Yapılması gereken iş: [doAjouter] yönteminin kodunu tamamlayın.
10.3.9.4. [Supprimer] bağlantısının durumunu yönetme
[Supprimer] - [9] bağlantısı, yalnızca [8]'te bir öğe seçildiğinde etkindir.
[AbstractElectionsSwing] sınıfında, [8] listesindeki bir öğeye tıklandığında tepki veren işleyici şöyledir:
private void jListNomsVoixValueChanged(javax.swing.event.ListSelectionEvent evt) {
doMajLabelSupprimer();
}
- satır, [ElectionsSwing] sınıfının [doMajLabelSupprimer] yöntemini çağırır.
@Override
protected void doMajLabelSupprimer() {
// [jLabelSupprimer] etiketini ve ilgili menü seçeneğini etkinleştir
...
}
Yapılması gereken iş: [doMajLabelSupprimer] yönteminin kodunu tamamlayın.
10.3.9.5. Aday listesini silme
[Supprimer] - [9] bağlantısı, (8) numaralı adımda seçilen (ad, ses) çiftini silmeye yarar. Silme işlemi tamamlandıktan sonra, [Supprimer] bağlantısı devre dışı kalır. Bu bağlantı, ancak (8) numaralı adımda yeni bir liste seçildiğinde yeniden etkinleştirilir.
![]() | ![]() |
[AbstractElectionsSwing] sınıfında, [Supprimer] bağlantısına tıklandığında tepki veren yönetici şudur:
private void jLabelSupprimerMouseClicked(java.awt.event.MouseEvent evt) {
if(jLabelSupprimer.isEnabled()){
doSupprimer();
}
}
- satır, [ElectionsSwing] sınıfının [doSupprimer] yöntemini çağırır.
@Override
protected void doSupprimer() {
// seçili liste, modèleNomsVoix şablonu ve girilen listeler siliniyor
...
// formdaki etiketlerin ve menü seçeneklerinin durumunun güncellenmesi
Utilitaires.setEnabled(...);
...
}
Yapılması gereken iş: [doSupprimer] yönteminin kodunu tamamlayın.
10.3.9.6. [Calculer] bağlantısının durumunu yönetme
[Calculer] - [10] bağlantısı, yalnızca [8]'te en az bir öğe olduğunda etkindir.
Yapılması gereken iş: Daha önce yazılan [doAjouter] ve [doSupprimer] yöntemlerine bu bağlantıyı yönetmek için gerekli kodu ekleyin. İlgili menü seçeneği de yönetilecektir.
Bilginiz olsun: DefaultListModel türündeki bir öğenin eleman sayısı, size() yöntemi ile elde edilir.
10.3.9.7. Koltuk sayısını hesaplama
[Calculer] [10] bağlantısı, koltuk sayımını başlatmaya ve sonuçları (14) numaralı alanda görüntülemeye olanak tanır. Hata durumunda (tüm listeler elenmişse), [15]'te bir hata mesajı görüntülenir. Her durumda, hesaplama tamamlandıktan sonra [Calculer] [10] bağlantısı devre dışı bırakılır.
[AbstractElectionsSwing] sınıfında, [Calculer] bağlantısına tıklandığında yanıt veren işleyici şudur:
private void jLabelCalculerMouseClicked(java.awt.event.MouseEvent evt) {
if(jLabelCalculer.isEnabled()){
doCalculer();
}
}
- satır, [ElectionsSwing] sınıfının [doCalculer] yöntemini çağırır.
// kullanıcı tarafından girilen listeler
private final List<ListeElectorale> listesSaisies = new ArrayList<>();
private ListeElectorale[] tListesSaisies;
...
@Override
protected void doCalculer() {
tListesSaisies = listesSaisies.toArray(new ListeElectorale[0]);
// koltuk sayısının hesaplanması
try {
...
} catch (ElectionsException ex) {
// istisna görüntülenir
...
return;
}
// sonuçların görüntülenmesi
...
// form durumunun güncellenmesi
Utilitaires.setEnabled(...);
}
Yapılması gereken iş: [doCalculer] yönteminin kodunu tamamlayın.
10.3.9.8. Sonuçları veri kaynağına kaydetme
[Enregistrer] (12) bağlantısı, koltuk hesaplama sonuçlarının veri kaynağına kaydedilmesini sağlar. Kayıt işlemi başarıyla tamamlandığında, [Enregistrer] bağlantısı devre dışı bırakılır. İşlem başarısız olursa, [15]'te bir hata mesajı görüntülenir. Her durumda, [Enregistrer] bağlantısı daha sonra devre dışı bırakılır.
[AbstractElectionsSwing] sınıfında, [Enregistrer] etiketine yapılan tıklamayı yöneten işleyici şöyledir:
private void jLabelEnregistrerMouseClicked(java.awt.event.MouseEvent evt) {
if(jLabelEnregistrer.isEnabled()){
doEnregistrer();
}
}
- satır, [ElectionsSwing] sınıfının [doEnregistrer] yöntemini çağırır:
@Override
protected void doEnregistrer() {
// iş mantığı katmanına kayıt talebi
try {
...
} catch (ElectionsException ex) {
// istisna görüntüleniyor
...
// grafik arayüze geri dönülür
return;
}
// formun güncellenmesi
Utilitaires.setEnabled(...);
...
}
Yapılması gereken iş: [doEnregistrer] yönteminin kodunu tamamlayın.
10.3.9.9. Sonuçları sil
[Effacer] (11) bağlantısı, (14) numaralı alanda görüntülenen sonuçları silmeye yarar.
[AbstractElectionsSwing] sınıfında, [Effacer] etiketine yapılan tıklamayı işleyen yönetici şöyledir:
private void jLabelEffacerMouseClicked(java.awt.event.MouseEvent evt) {
if(jLabelEffacer.isEnabled()){
doEffacer();
}
}
- satır, [ElectionsSwing] sınıfının [doEffacer] yöntemini çağırır:
@Override
protected void doEffacer() {
// sonuç listesi sıfırlanıyor
....
// formu güncelle
Utilitaires.setEnabled(...);
}
Yapılması gereken iş: [doEffacer] yönteminin kodunu tamamlayın.
Bilginiz olsun: DefaultListModel sınıfında, tüm öğelerini silen clear() yöntemi bulunmaktadır.
10.3.10. İyileştirmeler
Önceki grafik arayüz çeşitli şekillerde iyileştirilebilir: Kullanıcı, unutkanlık nedeniyle açılır menüdeki tüm listelerin oylarını giremeyebilir; ayrıca, yanlışlıkla aynı listenin oylarını birden fazla kez girebilir.
Yapılması gereken iş: Bu iki durumun ortaya çıkmaması için algoritmayı iyileştirin. Basit bir çözüm, girilen listelerin bir sözlüğünü yönetmektir; bu sözlüğün anahtarları, açılır menünün öğeleri olacaktır. Ayrıca, [Calculer] bağlantısının yalnızca tüm listeler girildiğinde etkinleştirilmesini sağlayacağız.
[ref1] dersinde okunacak bölüm: 3.8. paragrafındaki HashTable sınıfı.






































