Aus Linux-Magazin 04/2006

Nützliche Komponenten für Swing-Anwendungen

Swing-Anwendungen sehen etwas altbacken aus. Das muss nicht sein, denn es gibt Widgets, die Anwendungen nicht nur um nützliche Funktionen bereichern, sondern auch optisch aufpeppen.

Swing bringt eine Reihe von Standardwidgets mit: Labels, Buttons, Eingabefelder oder Tabellen. Damit lassen sich bereits viele Anwendungen schreiben. Doch viele Programmierer geben sich damit nicht zufrieden. Drei Gründe sprechen für die Aufrüstung des Standard-Java mit externen Zusätzen: Die verfügbaren Widgets lassen einige Wünsche offen, weil nützliche und oft benötigte Komponenten fehlen. Außerdem erhält die Anwendung eine persönliche Note, wenn verschiedene Komponenten zusammenspielen.

Schließlich spielt auch die Usability eine wichtige Rolle: Viele Nutzer kennen die bekannten Mail- und Browser-Suiten. Die Übernahme dieser Bedienkonzepte erspart ihnen eine Umgewöhnung.

Auf der Suche nach JTAN

Dieser Coffeeshop stellt einige Widgets aus externen Quellen vor. Wichtig bei der Auswahl war neben der freien Verfügbarkeit unter einer Open-Source-Lizenz aber vor allem die einfache Anwendung.

Wer seine Tex-Installation erweitern will, sucht das CTAN (Comprehensive Tex Archive Network) auf, die Perl-Kollegen das CPAN. Wo aber ist das JTAN? Für Java existiert leider kein zentrales Repository mit erneut verwendbaren Komponenten; es gibt zwar nützliche Sammlungen, aber die sind verstreut und nicht leicht zu finden. Auch tendiert Java mit seinem Fokus auf Wiederverwendbarkeit zu kompletten Frameworks; die installieren aber Programmierer nicht so schnell nach wie beispielsweise ein CPAN-Perl-Modul.

Da eine Suche nach »swing gui widgets« auf Freshmeat [1] lediglich zwei Ergebnisse fördert, greift dieser Artikel auf Java.net [2] zurück, eine von Sun gesponserten Seite. Java.net führt viele Projekte, insbesondere zum Thema Swing. Deren Qualität variiert stark, von gänzlich leeren bis zu anspruchsvollen Java-Erweiterungen. Auch Sun selbst stellt hier Projekte vor, die künftig zu den offiziellen Bibliotheken gehören könnten. Sie stehen jedoch nicht unter einer freien Lizenz und sind erst nach einer Anmeldung erreichbar.

Eine Leiste mit großen Icons hat sich zu einer beliebten Methode für den direkten Zugriff auf Anwendungen entwickelt. Bekannte Beispiele dafür sind Kontact, das unabhängige Programme wie KMail integriert, und das Windows-Programm Outlook. Auch in Java lässt sich dieses Konzept realisieren.

Das Beispielprogramm dieser Coffeeshop-Folge nutzt eine solche Button-Bar, um die verschiedenen Widgets vorzustellen (siehe Abbildung 1). »ButtonBar« stammt – ebenso wie zwei weiter unten vorgestellte Komponenten – aus der Widget-Sammlung von [3]. Sie teilt die einzelnen Erweiterungen so auf, dass sich benötigte Widgets einzeln herunterladen lassen. Alternativ steht die Datei »l2fprod-common-all.jar« zur Verfügung, die alle Komponenten enthält.

Abbildung 1: Eine Buttonleiste erleichtert in vielen Desktop-Programmen den direkten Zugriff auf integrierte Anwendungen. Per Erweiterung lassen sich Button-Bars auch mit Java-Swing umsetzen.

Abbildung 1: Eine Buttonleiste erleichtert in vielen Desktop-Programmen den direkten Zugriff auf integrierte Anwendungen. Per Erweiterung lassen sich Button-Bars auch mit Java-Swing umsetzen.

Das Widget »ButtonBar« befindet sich in der Bibliothek »l2fprod-common-buttonbar.jar«. Jede der Jar-Dateien enthält ein Beispielprogramm, das einen optischen Eindruck gibt und die Anwendung demonstriert.

Button-Bars für integrierte Anwendungen

»ButtonBar« selbst steuert leider nur die Anordnung der Buttons, den Wechsel der angezeigten Seite im anderen Fensterbereich muss der Java-Programmierer also selbst implementieren. Listing 1 zeigt die Klasse »ButtonBarPanel«, sie stellt die gesamte dazu notwendige Funktionalität zur Verfügung.

Listing 1:
»ButtonBarPanel.java«
22: import java.awt.*;
23: import java.awt.event.*;
24: import javax.swing.*;
25: import com.l2fprod.common.swing.*;
26:
34: public class ButtonBarPanel extends JPanel {
35:
36:   private JButtonBar iBar;
37:   private ButtonGroup iGroup;
38:   private Component   iCurrentComponent = null;
39:
40:   /////////////////////////////////////////
41:
46:   public ButtonBarPanel() {
47:     iBar = new JButtonBar(JButtonBar.VERTICAL);
48:     iGroup = new ButtonGroup();
49:     add("West",iBar);
50:   }
51:
52:   /////////////////////////////////////////
53:
58:   private void switch2Component(Component newComponent) {
59:     if (iCurrentComponent != null) {
60:       remove(iCurrentComponent);
61:     }
62:     iCurrentComponent = newComponent;
63:     add("Center",iCurrentComponent);
64:     revalidate();
65:     repaint();
66:   }
67:
68:   /////////////////////////////////////////
69:
74:   public void addPanel(final Component component,String title,String iconFile) {
75:     JToggleButton button = new JToggleButton();
76:     button.setAction(new AbstractAction(title,new ImageIcon(iconFile)) {
77:       public void actionPerformed(ActionEvent event) {
78:        switch2Component(component);
79:       }
80:     });
81:     iBar.add(button);
82:     iGroup.add(button);
83:
84:     // select first panel
85:     if (iGroup.getSelection() == null) {
86:       button.setSelected(true);
87:       switch2Component(component);
88:     }
89:   }
90: }

Der Constructor (Zeilen 46 bis 50) erstellt neben der eigentlichen »ButtonBar« eine »ButtonGroup«. Die »addPanel()«-Methode (Zeilen 74 bis 89) fügt die einzelnen Buttons zusammen mit der Hauptfensterkomponente hinzu. Den Wechsel zwischen den einzelnen Panels steuert dabei eine Action, die die Zeilen 76 bis 80 mit dem Button verknüpfen. Ein Klick auf den übergebenen Button führt die Methode »switch2Component()« (Zeilen 58 bis 66) aus, die das angezeigte Panel austauscht.

Diese Kapselung macht die Verwendung sehr einfach, wie Listing 2 beweist. Das erste praktische Widget dient auch als Rahmenprogramm für die Vorstellung weiterer Komponenten – jede Seite stellt etwas Nützliches vor. Neben der hier verwendeten »ButtonBar« gibt es eine an Outlook angelehnte Alternative, die sich ähnlich einsetzen lässt.

Listing 2:
»ButtonBarPanelPanel«
064:  private JPanel getButtonBar() {
065:    ButtonBarPanel bbp = new ButtonBarPanel();
066:    bbp.addPanel(getCalendarPanel(),"calendar","calendar.png");
067:    bbp.addPanel(getDirChooserPanel(),"directory","directory.png");
068:    bbp.addPanel(getFontChooserPanel(),"fonts","fonts.png");
069:    bbp.addPanel(getWizardPanel(),"wizard","wizard.png");
070:    return bbp;
071:  }

Ein schöner Kalender

Viele Anwendungen benötigten einen Kalender, damit der Nutzer ein Datum oder eine Zeitspanne auswählen kann. Der Einsatz des Widget »JCalendar« [4] spart viel Entwicklungszeit. In seiner einfachsten Form reicht der Constructor, den Kalender aus Abbildung 1 erzeugt diese Zeile:

Jcalendar cal = new JCalendar();

Das Kalender-Widget lässt sich auf verschiedene Arten konfigurieren, um das Aussehen anzupassen. Wer nicht die ganze API-Dokumentation studieren möchte, startet das in der Laufzeitbibliothek integrierte Beispielprogramm. Es konfiguriert alle Aspekte des Kalenders (Abbildung 2) und generiert dabei direkt den Code, der sich dann auf einfache Weise über das Clipboard ins eigene Programm einfügen lässt. Auch die bei einem Kalender wichtige Lokalisierung setzt »JCalendar« hervorragend um.

Abbildung 2: Das »JCalendar«-Beispielpro-gramm konfiguriert einen Kalender und generiert dabei den Code für eigene Anwendungen.

Abbildung 2: Das »JCalendar«-Beispielpro-gramm konfiguriert einen Kalender und generiert dabei den Code für eigene Anwendungen.

Mit Swing löste der anspruchsvollere Dateiauswahldialog »JFileChooser« den Vorgänger »FileDialog« aus AWT ab. Die Sun-Entwickler haben aber nicht daran gedacht, dass die Anwender gelegentlich keine Datei, sondern ein Verzeichnis auswählen möchten – dafür eignet sich »JFileChooser« nicht.

Ebenfalls von [3] stammt »DirectoryChooser«. Wer Erfahrung mit »JFileChooser« hat, kommt damit sofort zurecht. Listing 3 zeigt ein Anwendungsbeispiel, die Abbildung 3 den zugehörigen Dialog.

Listing 3:
»JDirectoryChooser«
106:  private void doSelectDir() {
107:    JDirectoryChooser dirChooser = new JDirectoryChooser();
108:    dirChooser.setMultiSelectionEnabled(true);
109:    int choice = dirChooser.showOpenDialog(this);
110:    if (choice == JDirectoryChooser.APPROVE_OPTION) {
111:      StringBuffer filenames = new StringBuffer();
112:      File[] selectedFiles = dirChooser.getSelectedFiles();
113:      for (int i = 0; i<selectedFiles.length; i++) {
114:        filenames.append("n");
115:        filenames.append(selectedFiles[i]);
116:      }
117:      JOptionPane.showMessageDialog(this,filenames);
118:    } else {
119:      JOptionPane.showMessageDialog(this,"selection cancelled");
120:    }
121:  }
Abbildung 3: »JDirectoryChooser« erlaubt die Auswahl von Verzeichnissen, der von Sun mitgelieferte »JFileChooser« markiert nur Dateien.

Abbildung 3: »JDirectoryChooser« erlaubt die Auswahl von Verzeichnissen, der von Sun mitgelieferte »JFileChooser« markiert nur Dateien.

Font-Auswahl

Auch der »JFontChooser« ist auf [3] zu finden, er erlaubt die Auswahl eines Fonts (Abbildung 4); das Sun-Java liefert dazu jedoch keine Hilfe mit. Mit »JFontChooser« genügt eine Zeile:

Font selectedFont = JFontChooser.showDialog(parent,"Choose Font",null);

Auch dieser Dialog ist bereits vollständig lokalisiert.

Die Widgetsammlung von [3] bietet noch weitaus mehr, darunter beispielsweise ein Tip-of-the-Day-Widget und einen Property-Sheet-Editor.

Abbildung 4: »JFontChooser« erlaubt die Auswahl eines Font. Suns Java liefert aber dafür keine hilfreiche Bibliothek mit.

Abbildung 4: »JFontChooser« erlaubt die Auswahl eines Font. Suns Java liefert aber dafür keine hilfreiche Bibliothek mit.

Wizards

In vielen Standardanwendungen kommen Wizards zum Einsatz, auf [2] gibt es mehrere Lösungen, eine vielleicht noch bessere ist auf [5] zu finden. Ein Wizard besteht aus verschiedenen Seiten (Panels), zwischen denen der Benutzer hin und her navigiert, in vielen Fällen über Verzweigungen durch die Benutzereingabe gesteuert. Auf jeder Seite kann der Anwender Einstellungen vornehmen. Ein Wizard muss also die Navigation unterstützen, die Ergebnisse der Benutzereingaben sammeln und am Ende dem aufrufenden Programm zur Verfügung stellen.

Das hier abgedruckte Beispiel beschränkt sich auf die einfachste Variante, es geht ja auch nur ums Prinzip. Eingabefelder und Buttons bekommen eigene Namen, wie es schon die AWT-Methode »java.awt.Component.getName()« vorsieht. Die Werte jedes Control kommen dann in eine Map, als Name fungiert der Schlüssel (Key).

In den Zeilen 186 bis 190 von Listing 4 erzeugt das Programm ein Array, das die Wizard-Seiten enthält. Sie leiten sich im einfachsten Fall von der Klasse »WizardPage« ab. Eine solche Wizard-Seite ist nichts anderes als ein »JPanel« mit den zusätzlichen Attributen Schritt-Nummer und Beschreibung. Letztere taucht im Wizard oben links auf (Abbildung 5).

Listing 4: Erstellen eines
Wizards
185:   private void doRunWizard(final JTextArea area) {
186:     WizardPage[] panels = new WizardPage[] {
187:       new WizardPanel("Page 1",1),
188:       new WizardPanel("Page 2",2),
189:       new WizardPanel("Page 3",3)
190:     };
191:
192:     WizardPage.WizardResultProducer resultCollector =
193:       new WizardPage.WizardResultProducer() {
194:         public Object finish(Map wizardData) {
195:           StringBuffer buf = new StringBuffer();
196:           buf.append("EntryField1: ");
197:           buf.append(wizardData.get("EntryField1"));
198:           buf.append("n");
199:           buf.append("EntryField2: ");
200:           buf.append(wizardData.get("EntryField2"));
201:           buf.append("n");
202:           buf.append("EntryField3: ");
203:           buf.append(wizardData.get("EntryField3"));
204:           area.setText(buf.toString());
205:           return area;
206:     }};
207:     WizardDisplayer.showWizard(WizardPage.createWizard(panels,resultCollector));
208:   }
Abbildung 5: Ein Java-Wizard lässt den Benutzer durch Einstellungsseiten navigieren und fragt Konfigurationsoptionen ab.

Abbildung 5: Ein Java-Wizard lässt den Benutzer durch Einstellungsseiten navigieren und fragt Konfigurationsoptionen ab.

Auswertung mit Callback

Die statische Methode »showWizard« vom »WizardDisplayer« zeigt den Wizard modal an (Zeile 207), eine weitere statische Methode mit dem Namen »createWizard« erzeugt ihn. Sie benötigt als Argumente das Array mit den Seiten und einen »WizardResultProducer«. Dieser enthält die Callback-Methode »finish()« für die Auswertung der oben beschriebenen Ergebnis-Map.

Im Beispiel gibt es drei Wizard-Seiten, jede enthält ein Eingabefeld namens »EntryField{1,2,3}«. Die Callback-Methode des »WizardResultProducer« steht in den Zeilen 194 bis 206. Sie fragt die Werte der Eingabefelder ab und schreibt sie in eine »JTextArea«.

Den kompletten Quellcode gibt es auf dem Listing-Server des Linux-Magazins [6]. Sind erst die Bibliotheken hinzugeladen und im Unterverzeichnis »lib« abgelegt, reicht ein einfacher Aufruf der beigefügten Skripte für das Kompilieren und Starten des Beispielprogramms.

finally{}

Es lohnt sich also durchaus, im Internet nach Java-Perlen zu fischen. Programmierer in vielen Ländern helfen Gleichgesinnten mit der Veröffentlichung ihres Code. Auch eine Release kleiner Klassen ist durchaus sinnvoll, wenn sie sich erneut verwenden lassen. (csc)

Infos
[1] Projektdatenbank Freshmeat: [http://www.freshmeat.net]

[2] Sammlung von Java-Projekten: [http://dev.java.net]

[3] Widget-Sammlung L2Fprod: [https://l2fprod-common.dev.java.net]

[4] JCalendar: [https://jcalendar.dev.java.net]

[5] Wizard-Implementation für Java: [https://wizard.dev.java.net]

[6] Komplette Quellen dieses Coffeeshop: [https://www.linux-magazin.de/Listings/2006/04/Coffeeshop]

Der Autor
Bernhard Bablok arbeitet bei der AGIS mbH als Anwendungsentwickler. Wenn er nicht Musik hört, mit dem Radl oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Objektorientierung.
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben