Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Neues in Java 7
2 Threads und nebenläufige Programmierung
3 Datenstrukturen und Algorithmen
4 Raum und Zeit
5 Dateien, Verzeichnisse und Dateizugriffe
6 Datenströme
7 Die eXtensible Markup Language (XML)
8 Dateiformate
9 Grafische Oberflächen mit Swing
10 Grafikprogrammierung
11 Netzwerkprogrammierung
12 Verteilte Programmierung mit RMI
13 RESTful und SOAP Web-Services
14 JavaServer Pages und Servlets
15 Applets
16 Datenbankmanagement mit JDBC
17 Technologien für die Infrastruktur
18 Reflection und Annotationen
19 Dynamische Übersetzung und Skriptsprachen
20 Logging und Monitoring
21 Java Native Interface (JNI)
22 Sicherheitskonzepte
23 Dienstprogramme für die Java-Umgebung
Stichwort
Download: - openbook, ca. 21,3 MB
Buch bestellen
Ihre Meinung?
<< zurück | Galileo Computing / <openbook> / Java 7 - Mehr als eine Insel | vor >>
Java 7 - Mehr als eine Insel von Christian Ullenboom Das Handbuch zu den Java SE-Bibliotheken
Java 7 - Mehr als eine Insel Galileo Computing 1433 S., 2012, geb. 49,90 Euro, ISBN 978-3-8362-1507-7 |
| 9 Grafische Oberflächen mit Swing
|  | 9.1 Fenster zur Welt
|  |  | 9.1.1 Swing-Fenster mit javax.swing.JFrame darstellen
|  |  | 9.1.2 Fenster schließbar machen – setDefaultCloseOperation()
|  |  | 9.1.3 Sichtbarkeit des Fensters
|  |  | 9.1.4 Größe und Position des Fensters verändern
|  |  | 9.1.5 Fenster- und Dialog-Dekoration, Transparenz *
|  |  | 9.1.6 Die Klasse Toolkit *
|  |  | 9.1.7 Dynamisches Layout während einer Größenänderung *
|  |  | 9.1.8 Zum Vergleich: AWT-Fenster darstellen *
|  | 9.2 Beschriftungen (JLabel)
|  |  | 9.2.1 Mehrzeiliger Text, HTML in der Darstellung
|  | 9.3 Icon und ImageIcon für Bilder auf Swing-Komponenten
|  |  | 9.3.1 Die Klasse ImageIcon
|  |  | 9.3.2 Die Schnittstelle Icon und eigene Icons zeichnen *
|  | 9.4 Es tut sich was – Ereignisse beim AWT
|  |  | 9.4.1 Die Ereignisquellen und Horcher (Listener) von Swing
|  |  | 9.4.2 Listener implementieren
|  |  | 9.4.3 Listener bei dem Ereignisauslöser anmelden/abmelden
|  |  | 9.4.4 Adapterklassen nutzen
|  |  | 9.4.5 Innere Mitgliedsklassen und innere anonyme Klassen
|  |  | 9.4.6 Aufrufen der Listener im AWT-Event-Thread
|  |  | 9.4.7 Ereignisse, etwas genauer betrachtet *
|  | 9.5 Schaltflächen
|  |  | 9.5.1 Normale Schaltflächen (JButton)
|  |  | 9.5.2 Der aufmerksame ActionListener
|  |  | 9.5.3 Schaltflächen-Ereignisse vom Typ ActionEvent
|  |  | 9.5.4 Basisklasse AbstractButton
|  |  | 9.5.5 Wechselknopf (JToggleButton)
|  | 9.6 Textkomponenten
|  |  | 9.6.1 Text in einer Eingabezeile
|  |  | 9.6.2 Die Oberklasse der Text-Komponenten (JTextComponent)
|  |  | 9.6.3 Geschützte Eingaben (JPasswordField)
|  |  | 9.6.4 Validierende Eingabefelder (JFormattedTextField)
|  |  | 9.6.5 Einfache mehrzeilige Textfelder (JTextArea)
|  |  | 9.6.6 Editor-Klasse (JEditorPane) *
|  | 9.7 Swing Action *
|  | 9.8 JComponent und Component als Basis aller Komponenten
|  |  | 9.8.1 Hinzufügen von Komponenten
|  |  | 9.8.2 Tooltips (Kurzhinweise)
|  |  | 9.8.3 Rahmen (Border) *
|  |  | 9.8.4 Fokus und Navigation *
|  |  | 9.8.5 Ereignisse jeder Komponente *
|  |  | 9.8.6 Die Größe und Position einer Komponente *
|  |  | 9.8.7 Komponenten-Ereignisse *
|  |  | 9.8.8 UI-Delegate – der wahre Zeichner *
|  |  | 9.8.9 Undurchsichtige (opake) Komponente *
|  |  | 9.8.10 Properties und Listener für Änderungen *
|  | 9.9 Container
|  |  | 9.9.1 Standardcontainer (JPanel)
|  |  | 9.9.2 Bereich mit automatischen Rollbalken (JScrollPane)
|  |  | 9.9.3 Reiter (JTabbedPane)
|  |  | 9.9.4 Teilungskomponente (JSplitPane)
|  | 9.10 Alles Auslegungssache: die Layoutmanager
|  |  | 9.10.1 Übersicht über Layoutmanager
|  |  | 9.10.2 Zuweisen eines Layoutmanagers
|  |  | 9.10.3 Im Fluss mit FlowLayout
|  |  | 9.10.4 BoxLayout
|  |  | 9.10.5 Mit BorderLayout in alle Himmelsrichtungen
|  |  | 9.10.6 Rasteranordnung mit GridLayout
|  |  | 9.10.7 Der GridBagLayoutmanager *
|  |  | 9.10.8 Null-Layout *
|  |  | 9.10.9 Weitere Layoutmanager
|  | 9.11 Rollbalken und Schieberegler
|  |  | 9.11.1 Schieberegler (JSlider)
|  |  | 9.11.2 Rollbalken (JScrollBar) *
|  | 9.12 Kontrollfelder, Optionsfelder, Kontrollfeldgruppen
|  |  | 9.12.1 Kontrollfelder (JCheckBox)
|  |  | 9.12.2 ItemSelectable, ItemListener und das ItemEvent
|  |  | 9.12.3 Sich gegenseitig ausschließende Optionen (JRadioButton)
|  | 9.13 Fortschritte bei Operationen überwachen *
|  |  | 9.13.1 Fortschrittsbalken (JProgressBar)
|  |  | 9.13.2 Dialog mit Fortschrittsanzeige (ProgressMonitor)
|  | 9.14 Menüs und Symbolleisten
|  |  | 9.14.1 Die Menüleisten und die Einträge
|  |  | 9.14.2 Menüeinträge definieren
|  |  | 9.14.3 Einträge durch Action-Objekte beschreiben
|  |  | 9.14.4 Mit der Tastatur: Mnemonics und Shortcut
|  |  | 9.14.5 Der Tastatur-Shortcut (Accelerator)
|  |  | 9.14.6 Tastenkürzel (Mnemonics)
|  |  | 9.14.7 Symbolleisten alias Toolbars
|  |  | 9.14.8 Popup-Menüs
|  |  | 9.14.9 System-Tray nutzen *
|  | 9.15 Das Model-View-Controller-Konzept
|  | 9.16 Auswahlmenüs, Listen und Spinner
|  |  | 9.16.1 Listen (JList)
|  |  | 9.16.2 Auswahlmenü (JComboBox)
|  |  | 9.16.3 Drehfeld (JSpinner) *
|  |  | 9.16.4 Datumsauswahl *
|  | 9.17 Tabellen (JTable)
|  |  | 9.17.1 Ein eigenes Tabellen-Model
|  |  | 9.17.2 Basisklasse für eigene Modelle (AbstractTableModel)
|  |  | 9.17.3 Ein vorgefertigtes Standard-Modell (DefaultTableModel)
|  |  | 9.17.4 Ein eigener Renderer für Tabellen
|  |  | 9.17.5 Zell-Editoren
|  |  | 9.17.6 Größe und Umrandung der Zellen *
|  |  | 9.17.7 Spalteninformationen *
|  |  | 9.17.8 Tabellenkopf von Swing-Tabellen *
|  |  | 9.17.9 Selektionen einer Tabelle *
|  |  | 9.17.10 Automatisches Sortieren und Filtern mit RowSorter *
|  | 9.18 Bäume (JTree)
|  |  | 9.18.1 JTree und sein TreeModel und TreeNode
|  |  | 9.18.2 Selektionen bemerken
|  |  | 9.18.3 Das TreeModel von JTree *
|  | 9.19 JRootPane und JDesktopPane *
|  |  | 9.19.1 Wurzelkomponente der Top-Level-Komponenten (JRootPane)
|  |  | 9.19.2 JDesktopPane und die Kinder von JInternalFrame
|  |  | 9.19.3 JLayeredPane
|  | 9.20 Dialoge und Window-Objekte
|  |  | 9.20.1 JWindow und JDialog
|  |  | 9.20.2 Modal oder nicht-modal?
|  |  | 9.20.3 Standarddialoge mit JOptionPane
|  |  | 9.20.4 Der Dateiauswahldialog
|  |  | 9.20.5 Der Farbauswahldialog JColorChooser *
|  | 9.21 Flexibles Java-Look-and-Feel
|  |  | 9.21.1 Look and Feel global setzen
|  |  | 9.21.2 UIManager
|  |  | 9.21.3 Die Windows-Optik mit JGoodies Looks verbessern *
|  | 9.22 Swing-Komponenten neu erstellen oder verändern *
|  |  | 9.22.1 Überlagerungen mit dem Swing-Komponenten-Dekorator JLayer
|  | 9.23 Die Zwischenablage (Clipboard)
|  |  | 9.23.1 Clipboard-Objekte
|  |  | 9.23.2 Mit Transferable auf den Inhalt zugreifen
|  |  | 9.23.3 DataFlavor ist das Format der Daten in der Zwischenablage
|  |  | 9.23.4 Einfügungen in der Zwischenablage erkennen
|  |  | 9.23.5 Drag & Drop
|  | 9.24 Undo durchführen *
|  | 9.25 AWT, Swing und die Threads
|  |  | 9.25.1 Ereignisschlange (EventQueue) und AWT-Event-Thread
|  |  | 9.25.2 Swing ist nicht thread-sicher
|  |  | 9.25.3 invokeLater() und invokeAndWait()
|  |  | 9.25.4 SwingWorker
|  |  | 9.25.5 Eigene Ereignisse in die Queue setzen *
|  |  | 9.25.6 Auf alle Ereignisse hören *
|  | 9.26 Barrierefreiheit mit der Java Accessibility API
|  | 9.27 Zeitliches Ausführen mit dem javax.swing.Timer
|  | 9.28 Die Zusatzkomponentenbibliothek SwingX
|  |  | 9.28.1 Im Angebot: Erweiterte und neue Swing-Komponenten
|  |  | 9.28.2 Überblick über erweiterte Standard-Swing-Klassen
|  |  | 9.28.3 Neue Swing-Klassen
|  |  | 9.28.4 Weitere SwingX-Klassen
|  |  | 9.28.5 SwingX-Installation
|  | 9.29 Alternativen zu programmierten Oberflächen, AWT und Swing *
|  |  | 9.29.1 Deklarative Beschreibungen der Oberfläche: Swing JavaBuilder, Swixml
|  |  | 9.29.2 SWT (Standard Widget Toolkit)
|  | 9.30 Zum Weiterlesen
## 9.16 Auswahlmenüs, Listen und Spinner Stehen dem Benutzer mehrere Möglichkeiten zur Auswahl, so gibt es GUI-Komponenten, die dem Benutzer genau diese Möglichkeiten zeigen. Das macht die Auwahl einfacher und schneller als ein Textfeld, denn aus einer Liste ist absehbar, welche Elemente genau existieren. Die Komponenten zur Auswahl beschreibt dieses Abschnitt. ### 9.16.1 Listen (JList) Eine JList zeigt in einer Spalte[73](Üblicherweise wählt die Liste eine Spalten-Darstellung. Eine JList kann jedoch die Einträge auch horizontal anordnen.) einige Einträge, aus denen der Benutzer wählen kann.
Beispiel
Erzeuge eine JList mit einigen Zeichenketten:
    String[] listData = { "Shinguz", "Glapum'tianer", "Suffus", "Zypanon", "Tschung" };
    JComponent jList = new JList( listData );
Statt dem Konstruktor in einem Feld die Daten zu geben, nimmt er auch Daten in einem Vector an. In Java 7 wird die JList als generischer Typ deklariert.
    class javax.swing.JList<E>
    extends JComponent
    implements Scrollable, Accessible
JList() Erzeugt eine neue leere List-Box. JList(Object[] listData) JList(Vector<? extends E> listData) Erzeugt eine Liste mit Daten, die dem Feld oder dem Vektor entstammen. Vor Java 7 lautete die Deklaration JList(Vector listData ). #### DefaultListModel als modifzierbares ListModel Eine JList verwaltet Einträge immer in einem Listen-Modell. Üblicherweise findet nicht der Konstruktor mit dem Objekt-Feld oder Vector Verwendung – der ein internes immutables Listen-Modell aufbaut –, sondern ein Konstruktor, der ein eigenes Listen-Modell annimmt. Ein Standard-Listenmodell, das von der API dem java.util.Vector zum Verwechseln ähnlich sieht, ist das DefaultListModel; Elemente lassen sich zu diesem Model einfach mit addElement()hinzufügen. Abbildung 9.36: Screenshot der Anwendung JListDemo Listing 9.47: com/tutego/insel/ui/list/JListDemo.java
    JFrame frame = new JFrame( "Levels of Happiness" );
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    final DefaultListModel<String> lykkeligModel = new DefaultListModel<String>();

    for ( String s : ("1. Denmark,2. Switzerland,3. Austria," +
                      "4. Iceland,5. Bahamas,35. Germany," +
                      "174. Ukraine,178. Burundi,Ende").split(",") )
      lykkeligModel.addElement( s );

    JList<String> list = new JList<String>( lykkeligModel );

    list.addListSelectionListener( new ListSelectionListener()
    {
      @Override public void valueChanged( ListSelectionEvent e ) {
        if ( e.getValueIsAdjusting() )
          return;

        System.out.println( e );

        if ( "Ende".equals( lykkeligModel.get( e.getLastIndex() ) ) )
          System.exit( 0 );
      }
    } );

    frame.add( new JScrollPane(list) );
    frame.pack();
    frame.setVisible( true );
Hinweis
Eine JList bietet im Gegensatz zur AWT-Liste keine Scroll-Möglichkeit. Enthält eine JList mehr Zeilen, als auf dem Bildschirm sichtbar sind, muss die JList in eine JScrollPane eingebettet werden. Gleiches gilt für ein JTextArea-Objekt.
Die JList zeigt die Einträge über einen Listen-Renderer an. Bei der Auswahl eines Eintrags und beim Doppelklick löst die Komponente ein ListSelectionEvent aus, das ein ListSelectionListener meldet. Die Methode getValueIsAdjusting() vom ListSelectionEvent liefert true, wenn der Benutzer den Selektionsvorgang noch nicht abgeschlossen hat, weil er zum Beispiel mit einem Mausklick ein Element selektiert hat und mit gedrückter Maustaste auf andere Elemente geht. Aus dem ListSelectionEvent erfragt getLastIndex() das Ende des Selektionsbereiches, sodass get() vom Listen-Model das Element erfragen kann.
    class javax.swing.JList<E>
    extends JComponent
    implements Scrollable, Accessible
JList(ListModel<E> dataModel) Erzeugt die Liste mit einem spezifischen Model. Da das Model die Daten beinhaltet, fehlen der JList-Komponente die Modifikationsmethoden, und es gibt auch keine Durchreichemethoden an das Model. Abbildung 9.37: UML-Diagramm von DefaultListModel #### Einen Eintrag selektieren Eine JList kann nicht nur ein selektiertes Element besitzen, sondern auch eine Gruppe von Elementen (Intervall genannt) kann markiert sein. Welche Selektionsart möglich ist, bestimmt ein internes ListSelectionModel, das drei Konstanten deklariert: SINGLE_SELECTION: Nur ein Element darf zur gleichen Zeit selektiert sein. SINGLE_INTERVAL_SELECTION: Eine zusammenhängende Gruppe von Elementen darf selektiert sein. MULTIPLE_INTERVAL_SELECTION: Auch nicht zusammenhängende Elemente können selektiert sein. Die Methode setSelectionMode(int selectionMode) setzt eine neue Selektionsart bei der JList, wobei eine der drei genannten Konstanten übergeben wird.
    class javax.swing.JList<E>
    extends JComponent
    implements Scrollable, Accessible
void setSelectionMode(int selectionMode) Setzt den Selektionsmodus. Das Argument kann die Werte ListSelectionModel.SINGLE_SELECTION (nur ein Eintrag), SINGLE_INTERVAL_SELECTION (mehrere Werte, aber in einem Intervall) oder, was der Standard ist, MULTIPLE_INTERVAL_SELECTION (beliebige Anzahl von Selektionen) annehmen. int getSelectionMode() Liefert den Selektionsmodus. #### Ereignis bei einer Selektion Bei jeder Änderung der Selektion meldet die JList ein ListSelectionEvent, auf das ein ListSelectionListener reagiert. Die Erweiterung von EventObject deklariert drei Methoden:
    class javax.swing.event.ListSelectionEvent
    extends EventObject
int getFirstIndex() int getLastIndex() boolean getValueIsAdjusting() Einer JList wird der Listener über addListSelectionListener() hinzugefügt.
    class javax.swing.JList<E>
    extends JComponent
    implements Scrollable, Accessible
void addListSelectionListener(ListSelectionListener listener) Fügt einen Listener hinzu. void removeListSelectionListener(ListSelectionListener listener) Entfernt den Listener. Die Methoden haben wir im Beispielprogramm schon genutzt. #### Zugriff auf selektierte Elemente und der Selektionsmodus Außer im Ereignis-Listener auf die Selektionseigenschaften zuzugreifen, bietet die JList einige praktische Methoden (wie getSelectedIndex()), die bei einer einfachen Selektion direkt den Index des markierten Elements geben.
    class javax.swing.JList<E>
    extends JComponent
    implements Scrollable, Accessible
void clearSelection() Löscht vorgenommene Selektionen. int getSelectedIndex() Liefert die Position des selektierten Eintrags oder –1, wenn kein Element gewählt wurde. int[] getSelectedIndices() Liefert die Positionen aller selektierten Einträge. Object getSelectedValue() Liefert den selektieren Eintrag oder null. Ab Java 7 wird sie veraltet sein. List<E> getSelectedValuesList() Neue Methode in Java 7, die alle selektierten Einträge liefert. (Vor Java 7 wurde hier die nicht-generische Methode getSelectedValues() eingesetzt, die ein Object[] als Rückgabe hatte.) void ensureIndexIsVisible(int index) Bewegt die Liste in einem Ausschnitt, sodass der Eintrag an der Stelle index sichtbar ist. #### Beispiel mit Textfeld, Schaltfläche und Liste Eine Oberfläche soll eine Schaltfläche zum Löschen von selektierten Elementen einer Liste anbieten, genauso wie ein Textfeld zum Eintragen neuer Strings. Wir können dazu drei Swing-Komponenten vorsehen und ein DefaultListModel als Datenbehälter: Listing 9.48: com/tutego/insel/ui/list/JListAddElementsDemo.java
    final DefaultListModel<String> listModel = new DefaultListModel<String>();
    final JList<String> list = new JList<String>( listModel );
    JButton     btn  = new JButton( "Remove" );
    JTextField  tf   = new JTextField();
Für die Schaltfläche lässt sich folgender Ereignisbehandler vorsehen:
    btn.addActionListener( new ActionListener() {
      @Override public void actionPerformed( ActionEvent e ) {
        int index = list.getSelectedIndex();
        if ( index == –1 )
          return;
        listModel.remove( index );
      }
    } );
Gibt es kein selektiertes Element, liefert getSelectedIndex() die Rückgabe –1, und die Methode beendet die Verarbeitung. Für das Textfeld nimmt der ActionListener einfach den Text heraus und setzt ihn per addElement() in das DefaultListModel.
    tf.addActionListener( new ActionListener()
    {
      @Override public void actionPerformed( ActionEvent e )
      {
        String text = ((JTextField)e.getSource()).getText();
        listModel.addElement( text );
        ((JTextField)e.getSource()).setText( "" );
      }
    } );
#### Renderer Die Aufgabe eines Renderers ist es, die Elemente darzustellen. Standardmäßig nutzt die JList ein spezielles JLabel zur Darstellung, das die toString()-Methode auf jedem Listenelement aufruft und darstellt. Ein eigener Renderer ist leicht implementiert: Zunächst ist eine Klasse zu schreiben, die ListCellRenderer implementiert – am besten über die abstrakte Basisklasse DefaultListCellRenderer. Die JList-Methode setCellRenderer(ListCellRenderer) setzt ihn dann und verweist bei der Darstellung eines Eintrags auf dieses Objekt. Abbildung 9.38: Die Schnittstelle ListCellRenderer Abbildung 9.39: DefaultListCellRenderer erweitert JLabel und implementiert ListCellRenderer. Ein Render-Beispiel gibt Abschnitt 9.17.4, »Ein eigener Renderer für Tabellen«, und auch die API-Dokumentation zeigt bei ListCellRenderer ein einfaches Beispiel. ### 9.16.2 Auswahlmenü (JComboBox) Ein Auswahlmenü (engl. choice box, auch combo box) zeigt eine Zeichenkette aus einer Liste von Möglichkeiten an. Wird die Choice-Box aufgeklappt, kann ein Element aus der List-Box gewählt werden. Ein neuer Eintrag erscheint dann im Titel des Menüs. Die JComboBox ist das Swing-Auswahlmenü, das optional ein Textfeld zur Eingabe anbietet. In diesem Textfeld können Texte in beliebigen Modellen dargestellt und ausgewählt werden; ein Tastendruck lässt die Liste zu dem Eintrag springen, dessen Buchstabe eingegeben wurde. Ob das Textfeld editiert werden kann, bestimmt setEditable(). Befinden sich zu viele Einträge in der Liste, stellt Swing automatisch eine scrollende Liste dar. Ab welcher Anzahl von Elementen die scrollende Liste dargestellt wird, bestimmt setMaximumRowCount(). Mit addItem() lassen sich Elemente dem assoziierten ComboBoxModel hinzufügen, mit removeItem() lassen sie sich wieder entfernen, und getItemAt(index) erfragt ein Element. Das aktuell ausgewählte Element erfahren wir mit getSelectedItem() und den Index mit getSelectedIndex(). Abbildung 9.40: Beispiel einer JComboBox Beim Auswählen eines Eintrags wird ein Action- und ItemEvent ausgelöst, mit dem wir das ausgewählte Objekt erfragen können: Listing 9.49: com/tutego/insel/ui/list/JComboBoxDemo.java, main()
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    String[] lang = {
      "Patronengurt", "Hausnummer", "Schmetterling", "Sphinx", "Anbetung",
      "Ende"
    };

    // Nicht editierbare JComboBox
    JComboBox<String> combo1 = new JComboBox<String>();

    for ( String s : lang )
      combo1.addItem( s );

    combo1.addActionListener( new ActionListener() {
      @Override public void actionPerformed( ActionEvent e )
      {
        System.out.println( e );
        JComboBox<?> selectedChoice = (JComboBox<?>) e.getSource();
        if ( "Ende".equals( selectedChoice.getSelectedItem() ) )
          System.exit( 0 );
      }
    } );

    frame.add( combo1, BorderLayout.LINE_START );

    // Editierbare JComboBox
    JComboBox<String> combo2 = new JComboBox<String>( lang );
    combo2.setEditable( true );
    combo2.setSelectedItem( "Sphinx" );
    combo2.setMaximumRowCount( 4 );

    frame.add( combo2, BorderLayout.LINE_END );

    frame.pack();
    frame.setVisible( true );
Die Methode addItem() funktioniert nur dann, wenn im Konstruktor kein spezielles Model angegeben wurde. Mit Modellen werden wir uns zu einem späteren Zeitpunkt näher beschäftigen. Sehen wir uns zunächst die Konstruktoren an. Seit Java 7 ist die Klasse generisch deklariert.
    class javax.swing.JComboBox<E>
    extends JComponent
    implements ItemSelectable, ListDataListener, ActionListener, Accessible
JComboBox() Erzeugt ein leeres Auswahlmenü mit einem Standard-Model. JComboBox(E[] items) Erzeugt ein Auswahlmenü mit Elementen, die im Feld items angegeben sind. JComboBox(Vector<?> items) Erzeugt ein Auswahlmenü mit Elementen, die im Vektor items abgelegt sind. JComboBox(ComboBoxModel<E> aModel) Erzeugt ein Auswahlmenü mit einem Combo-Box-Model, das die Daten speichert. Zur Selektion beziehungsweise Abfrage selektierter Elemente bietet die Klasse weitere Methoden an: Object getSelectedItem() Liefert die aktuelle Wahl zurück. Object[] getSelectedObjects() Liefert ein Array mit den selektierten Einträgen. int getSelectedIndex() Liefert den Index des aktuell selektierten Eintrags. void setSelectedIndex(int position) Setzt den Eintrag im Titel des Menüs auf den Eintrag mit der Nummer position. void setSelectedItem(Object anObject) Setzt den Eintrag string in die Titelleiste, wenn dieser in der Liste ist. Falls mehrere Einträge gleich dem string sind, wird jener Eintrag verwendet, der zuerst gefunden wurde. Dieser besitzt dann also den kleinsten Index. #### Durchreichemethoden an das Model Elemente lassen sich jetzt hinzufügen und löschen. Etwas seltsam erscheint uns die Tatsache, dass die JComboBox selbst diese Methoden anbietet. Ähnliche Swing-Komponenten (wie die JList oder JTable) bieten diese Anfrage- und Veränderungsmethoden nicht an, sondern erwarten direkt eine Änderung am Model und nicht an der Komponente. Das Besondere an Swing-Komponenten ist ja gerade, dass das Model verändert und abgefragt wird. Im Fall der JComboBox speichert sie die Daten natürlich immer noch nicht selbst, sondern leitet sie an das Model weiter. So sind diese Methoden nur Durchreichemethoden.
    class javax.swing.JComboBox<E>
    extends JComponent
    implements ItemSelectable, ListDataListener, ActionListener, Accessible
void addItem(E item) Fügt dem Model einen Eintrag hinzu. E getItemAt(int index) Liefert den Eintrag an der Position index. Die Rückgabe ist null, wenn sich der Index außerhalb des Bereichs befindet. int getItemCount() Liefert die Anzahl der Einträge im Auswahlmenü. void insertItemAt(E item, int index) Fügt einen Eintrag an einer bestimmten Stelle ein. void removeItem(Object anObject) Löscht den Eintrag aus der Liste. void removeItemAt(int position) Löscht den Eintrag an der Position position. void removeAll() Entfernt alle Einträge aus dem Auswahlmenü. Die Methoden zum Modifizieren des Models funktionieren nur dann, wenn das ComboBoxModel insbesondere ein MutableComboBoxModel ist. Abbildung 9.41: Komplexe Typbeziehung von DefaultComboBoxModel #### Ereignisse der JComboBox Die JComboBox löst bei der Selektion zwei Arten von Ereignissen aus: ActionEvent und ItemEvent. Zudem lässt sich ein PopupMenuListener hinzufügen. Zum An- und Abmelden dienen die üblichen Methoden. Der Unterschied in den Ereignissen ActionEvent und ItemEvent ist: Der ActionListener empfängt nur ein Ereignis bei einem neu gewählten Element. Der ItemListener empfängt alle Veränderungen bei Selektionen. Das heißt, wenn der Benutzer ein neues Element anwählt, gibt es ein Ereignis für die Deselektion und eines für die Selektion. Geht der Benutzer etwa mit den Cursor-Tasten durch die Liste, rasselt es an Ereignissen.
    class javax.swing.JComboBox<E>
    extends JComponent
    implements ItemSelectable, ListDataListener, ActionListener, Accessible
void addItemListener(ItemListener aListener) void removeItemListener(ItemListener aListener) void addActionListener(ActionListener l) void removeActionListener(ActionListener l) Wenn die Box mit setEditable(true) editierbar gemacht wurde und der Benutzer im Textfeld eine Eingabe getätigt hat, wird ebenfalls der ActionListener informiert. Dann lässt sich jedoch nicht mehr direkt der Ursprung des ActionEvent ersehen. Standardmäßig bleibt die Selektion unverändert, und insbesondere wird das Element nicht automatisch in das Model übertragen. #### Zuordnung einer Taste mit einem Eintrag * Bei der Benutzung einer JComboBox möchten Benutzer gern Elemente schnell per Tastatur auswählen. Ist etwa eine sortierte Auswahl mit Zeichenketten gegeben, sollte ein getippter Buchstabe zum ersten Eintrag führen, der mit diesem Buchstaben beginnt. Diese Funktionalität ist in Swing schon vorprogrammiert, kann aber noch erweitert werden. Dazu gilt es, einen KeySelectionManager zu implementieren. Dieser verbindet mit der gedrückten Taste (char) einen Index in der ComboBox (int). Unser nächstes Beispiel soll zeigen, wie eine Liste mit den ersten Buchstaben des Alphabets mit der Tastenauswahl 1, 2, 3 verbunden wird. Der interessanteste Teil ist die innere Klasse, die die Schnittstelle JComboBox.KeySelectionManager implementiert: Listing 9.50: com/tutego/insel/ui/list/JComboBoxKeySelection.java
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    JComboBox<String> cb = new JComboBox<String>( "A,B,C".split(",") );

    cb.setKeySelectionManager( new JComboBox.KeySelectionManager()
    {
      @Override public int selectionForKey( char aKey, ComboBoxModel aModel )
      {
        int pos = Math.abs( aKey – 1 – '0' );
        return pos >= aModel.getSize() ? aModel.getSize() – 1 : pos;
      }
    } );

    frame.add( cb );
    frame.pack();
    frame.setVisible( true );
Die Methode selectionForKey() bekommt den ausgewählten Buchstaben und das Model. Dieses Model ist nicht so unnötig, wie es auf den ersten Blick erscheint, denn es ist wichtig, aus ihm die maximale Anzahl an Elementen auszulesen, damit es zu keiner fehlerhaften Rückgabe kommt. Eine Bedingung testet, ob eine Auswahl getätigt wird, die zu einem ungültigen Eintrag führen würde. #### Auswahlmenü mit Separator Standardmäßig unterstützt die JList und auch JComboBox keine Separatoren, doch die Unterteilung in Segmente ist in der Praxis sehr nützlich. Microsoft Word und PowerPoint benutzen sie zum Beispiel, um die zuletzt vom Benutzer ausgewählten Zeichensätze prominent oben in der Liste zu haben (Excel dagegen tut dies nicht). Abbildung 9.42: Separator in Words Zeichensatzauswahlliste Wir wollen diese Möglichkeit nachbilden und dabei noch einiges über Modelle und Renderer lernen. Abbildung 9.43: Eine eigene Variante mit JSeparator in JComboBox Bei der Umsetzung gibt es unterschiedliche Varianten, die sich außer in der technischen Implementierung darin unterscheiden, ob das Modell eine Markierung für den Separator enthält oder nicht. Wir stellen beide Ansätze vor und beginnen mit der ersten Variante, also damit, einem Zellen-Renderer Positionen mitzugeben, die sagen, wo ein Trennstrich zu zeichnen ist. Listing 9.51: com/tutego/insel/ui/list/JComboBoxWithSeparator1.java, main()
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    String[] items = { "Cambria", "Arial", "Verdana", "Times" };
    JComboBox<String> comboBox = new JComboBox<String>( items );
    ListCellRenderer<String> renderer = new SeparatorAwareListCellRenderer1<String>(
                                          comboBox.getRenderer(), 0 );
    comboBox.setRenderer( renderer );
    frame.add( comboBox );
    frame.pack();
    frame.setVisible( true );
Die eigene Klasse SeparatorAwareListCellRenderer1 ist ein ListCellRenderer, den die JComboBox zur Darstellung der Komponenten nutzt. Im Konstruktor des Renderers geben wir den Original-Renderer mit – es kann ein bestimmter Renderer schon vorinstalliert sein, den wollen wir dekorieren – sowie eine variable Argumentliste von Positionen. Das Beispiel übergibt nur 0, da nach dem ersten Element (Index = 0) ein Trennzeichen zu setzen sein soll, sodass Cambria und Arial abgetrennt sind. Listing 9.52: com/tutego/insel/ui/list/SeparatorAwareListCellRenderer1.java
    package com.tutego.insel.ui.list;

    import java.awt.*;
    import java.util.Arrays;
    import javax.swing.*;

    public class SeparatorAwareListCellRenderer1<E> implements ListCellRenderer<E>
    {
      private final ListCellRenderer<? super E> delegate;
      private final int[] indexes;
      private final JPanel panel = new JPanel( new BorderLayout() );

      public SeparatorAwareListCellRenderer1( ListCellRenderer<? super E> delegate,
                                              int... indexes )
      {
        Arrays.sort( indexes );
        this.delegate = delegate;
        this.indexes  = indexes;
      }

      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public Component getListCellRendererComponent( JList list, E value,
                                                     int index, boolean isSelected,
                                                     boolean cellHasFocus )
      {
        panel.removeAll();
        panel.add( delegate.getListCellRendererComponent( list, value, index,
                                                          isSelected, cellHasFocus ) );
        if ( Arrays.binarySearch( indexes, index ) >= 0 )
          panel.add( new JSeparator(), BorderLayout.PAGE_END );

        return panel;
      }
    }
Die Implementierung basiert auf der Idee, jede Komponente in einen Container (JPanel) zu setzen und in diesem Container dann je nach Bedarf ein JSeparator unten an diesem Container anzufügen. Statt JPanel mit einem JSeparator auszustatten, kann ebenfalls auch ein Border unten gezeichnet werden. Die Anweisung Arrays.binarySearch(indexes, index) >= 0 ist als »contains« zu verstehen, also als ein Test, ob der Index im Feld ist – leider gibt es so eine Methode nicht in der Java API. Wenn der Index im Feld ist, soll der Separator unter der Komponente erscheinen. Dass sich eine Trennlinie auch am Anfang befinden kann, berücksichtigt die Lösung nicht; diese Variante bleibt als Übungsaufgabe für die Leser. Diese Lösung ist einfach und funktioniert gut, denn vorhandene Renderer werden weiterverwendet, was sehr wichtig ist, denn größere Swing-Anwendungen nutzen viele eigene Renderer, etwa um Icons und Text zusammenzufassen. Ein Nachteil ist, dass der Separator zu einen Element gehört, und wenn das Element etwa in der Liste ausgewählt wird, steht der Separator mit in der Selektion und ist nicht abgetrennt (das ist aber bei Word genauso). Während die vorgestellte Variante in der Praxis gut funktioniert, wollen wir uns noch mit einer alternativen Umsetzung beschäftigen. Sie ist deutlich komplexer und auch nicht so flexibel. Die Lösung basiert auf der Idee, dass die Modelldaten eine Markierung für den Separator enthalten – die folgende Lösung nutzt null dafür. Da JList oder JComboBox eine null problemlos verträgt, ist die Basis schnell umgesetzt: Listing 9.53: com/tutego/insel/ui/list/JComboBoxWithSeparator1.java, main()
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    String[] items = { "Cambria", null, "Arial", "Cambria", "Verdana", "Times" };
    JComboBox<String> comboBox = new JComboBox<String>( items );
    frame.add( comboBox );
    frame.pack();
    frame.setVisible( true );
Wenn wir das Programm starten, zeigt es dort, wo das Model eine null liefert, einfach nichts an. Die Selektion dieses null-Elements ist auch möglich und führt zu keiner Ausnahme. Damit Swing das null-Element nicht als Leereintrag anzeigt, verpassen wir unserer JComboBox einen Zellen-Renderer. Der soll immer dann, wenn das Element null ist, ein Exemplar von JSeparator zeichnen. Vom Design ist es das beste, wenn der Zell-Renderer selbst die null-Elemente darstellt, aber das Zeichnen der Nicht-null-Elemente an einen anderen Zell-Renderer abgibt, anstatt diese etwa durch einen BasicComboBoxRenderer (ein JLabel, das ListCellRenderer implementiert) selbst zu renderen – das würde die Flexibilität der Lösung massiv einschränken. Listing 9.54: com/tutego/insel/ui/list/SeparatorAwareListCellRenderer2.java
    package com.tutego.insel.ui.list;

    import java.awt.Component;
    import javax.swing.*;

    public class SeparatorAwareListCellRenderer2<E> implements ListCellRenderer<E>
    {
      private final ListCellRenderer<? super E> delegate;

      public SeparatorAwareListCellRenderer2( ListCellRenderer<? super E> delegate )
      {
        this.delegate = delegate;
      }

      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public Component getListCellRendererComponent( JList list, E value,
                                                     int index, boolean isSelected,
                                                     boolean cellHasFocus )
      {
        if ( value == null )
          return new JSeparator();

        return delegate.getListCellRendererComponent( list, value, index,
                                                      isSelected, cellHasFocus );
      }
    }
Die eigene Klasse SeparatorAwareListCellRenderer2 bekommt einen anderen ListCellRenderer übergeben und liefert die Komponenten dieses Delegate-Renderers immer dann, wenn das Element ungleich null ist. Ist es dagegen gleich null, liefert getListCellRendererComponent() ein Exemplar des JSeparators. Im Beispielprogramm muss der Renderer nun angemeldet werden, und dazu ist Folgendes zu ergänzen: Listing 9.55: com/tutego/insel/ui/list/JComboBoxWithSeparator.java, Erweiterung
    comboBox.setRenderer(
      new SeparatorAwareListCellRenderer2<String>(comboBox.getRenderer()) );
Nach dem Start des Programms ist das Ergebnis schon viel besser: Dort, wo vorher die JComboBox eine leere Zeile darstellte, ist nun ein Strich. Die Lösung ist jedoch nur ein Etappensieg, denn die Navigation mit der Tastatur durch die Liste zeigt eine Schwachstelle: Das null-Element lässt sich auswählen und erscheint auch als Linie im Editor/Textfeld. Beheben lässt sich das Problem nicht mit dem Renderer, denn der ist nur mit der Darstellung in der Liste beauftragt. Hier muss in die Interna der Swing-Komponente eingegriffen werden. Jede Swing-Komponente hat ein korrespondierendes UI-Delegate, das für das Verhalten und die Darstellung der Komponente verantwortlich ist. Für die JComboBox sind das Unterklassen von ComboBoxUI, und zwei Methoden sind besonders interessant: selectNextPossibleValue() und selectPreviousPossibleValue(). Da jede UI-Implementierung ihr eigenes Look and Feel (LAF) mitbringt, müssen wir hier eigentlich einen Dekorator bauen und jede Methode bis auf die beiden genannten an das Original weiterleiten, doch das ist jetzt zu viel Arbeit, und so nehmen wir die Basisklasse WindowsComboBoxUI als Basisklasse, denn unser Beispiel nutzt das Windows-LAF. In der Unterklasse implementieren wir eigene Versionen der Methoden selectNextPossibleValue() und selectPreviousPossibleValue(), die so lange die Liste nach oben/unten laufen müssen, bis sie ein Element ungleich null finden. Listing 9.56: com/tutego/insel/ui/list/SeparatorAwareComboBoxUI.java
    package com.tutego.insel.ui.list;

    import com.sun.java.swing.plaf.windows.WindowsComboBoxUI;

    public class SeparatorAwareComboBoxUI extends WindowsComboBoxUI
    {
      @Override
      protected void selectNextPossibleValue()
      {
        for ( int index = comboBox.getSelectedIndex() + 1;
              index < comboBox.getItemCount();
              index++ )
          if ( comboBox.getItemAt( index ) != null )
          {
            comboBox.setSelectedIndex( index );
            break;
          }
      }

      @Override
      protected void selectPreviousPossibleValue()
      {
        for ( int index = comboBox.getSelectedIndex() – 1;
              index >= 0;
              index-- )
          if ( comboBox.getItemAt( index ) != null )
          {
            comboBox.setSelectedIndex( index );
            break;
          }
      }
    }
Das UI-Objekt muss ebenfalls angemeldet werden: Listing 9.57: com/tutego/insel/ui/list/JComboBoxWithSeparator.java, Erweiterung
    comboBox.setUI( new SeparatorAwareComboBoxUI() );
Enthält die Liste null-Elemente, überspringt die Tastennavigation über die Cursor-Taste diese. Doch auch mit diesem Teilstück fehlt ein weiteres Detail: Mit einem Klick lässt sich die Linie doch noch auswählen. Das ist keine Frage des Renderers und auch keine Frage der Tastaturnavigation – es muss untersagt werden, dass bei der Aktivierung ein null-Element zum Editor kommen kann. Hier ist die Methode setSelectedItem() von JComboBox entscheidend. Denn jedes Element, das selektiert wird – und dadurch auch in das Textfeld kommt –, geht durch die Methode hindurch. Wenn wir die Methode überschreiben und bei null-Elementen einfach nichts tun, wird auch das null-Element nicht selektiert, und im Textfeld bleibt das letzte Element stehen. Damit auch spezielle Implementierungen von JComboBox von diesem Verhalten profitieren können, müssen wir wieder einen Dekorator schreiben, doch das kostet zu viel Mühe, und so überschreibt eine einfache Unterklasse die setSelectedItem()-Methode. (Im Prinzip wäre auch eine überschriebene Methode von setSelectedIndex() sinnvoll, denn das könnte eine programmierte Aktivierung von null vermeiden.) Listing 9.58: com/tutego/insel/ui/list/SeparatorAwareJComboBox.java
    package com.tutego.insel.ui.list;

    import javax.swing.*;

    public class SeparatorAwareJComboBox<E> extends JComboBox<E>
    {
      public SeparatorAwareJComboBox( E... items )
      {
        super( items );
      }

      @Override
      public void setSelectedItem( Object anObject )
      {
        if ( anObject != null )
          super.setSelectedItem( anObject );
      }
    }
Im Hauptprogramm muss nur diese spezielle Klasse zum Einsatz kommen: Listing 9.59: com/tutego/insel/ui/list/JComboBoxWithSeparator.java, Erweiterung
    JComboBox<String> comboBox = new SeparatorAwareJComboBox<String>( items );
### 9.16.3 Drehfeld (JSpinner) * Ein JSpinner ist eine Drehfeld-Komponente und besteht aus einem Eingabefeld (Editor) mit zwei kleinen Pfeilen, die eine Veränderung der Werte erlauben. Entweder trägt der Nutzer in das Textfeld eine gültige Zahl ein, die mit den Pfeilen verändert werden kann, oder es wird eine Liste von Auswahlelementen angezeigt, aus denen der Benutzer wählen kann. Am nächsten sind JSpinner mit Combo-Boxen verwandt, doch geht bei ihnen kein Popup-Menü auf. #### SpinnerModel Ein JSpinner arbeitet auf einem Model vom Typ SpinnerModel, das ähnlich wie ein Iterator den Zugriff auf die Elemente ermöglicht. Ein SpinnerModel wird im Konstruktor von JSpinner gesetzt oder über setModel(). #### SpinnerListModel Ein vorgefertigtes Model ist das SpinnerListModel, das mit einem Feld initialisiert wird.
Beispiel
Erfrage alle Wochentage über die Klasse DateFormatSymbols. Das Feld von Zeichenfolgen soll dann ein SpinnerModel initialisieren. Dieses Model soll dem JSpinner im Konstruktor übergeben werden:
    String[]     days    = new DateFormatSymbols().getWeekdays();
    SpinnerModel model   = new SpinnerListModel( days );
    JSpinner     spinner = new JSpinner( model );
#### SpinnerDateModel Ein weiteres Model ist das SpinnerDateModel. Es erlaubt dem Benutzer lokalisierte Datumseingaben. Der JSpinner zeigt dann die Eingabezeile an, und das Model speichert die Benutzereingabe und bietet Abfragemethoden, um das gewählte Datum abzufragen.
Beispiel
Initialisiere den JSpinner mit einem SpinnerDateModel, und erfrage das gesetzte Datum:
    SpinnerDateModel model   = new SpinnerDateModel();
    JSpinner         spinner = new JSpinner( model );
    Date             value   = model.getDate();
Der Konstruktor SpinnerDateModel(Date value, Comparable start, Comparable end, int stepSize) erlaubt die Angabe eines Start- und eines Enddatums. Sie können null sein, wenn keine Grenzen gewünscht sind. Die Variable stepSize gibt an, welches Format zu editieren ist. Dahinter stehen viele Konstanten aus Calendar, wie etwa YEAR, HOUR oder WEEK_OF_MONTH. Abbildung 9.44: Screenshot der Anwendung JSpinnerDemo Listing 9.60: com/tutego/insel/ui/swing/JSpinnerDemo.java, main()
    JFrame f = new JFrame();
    f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    f.setLayout( new BoxLayout(f.getContentPane(), BoxLayout.Y_AXIS) );

    // Spinner for numbers from 1 to 10 in steps of 0.2

    SpinnerNumberModel model1 = new SpinnerNumberModel( 5.0, 0.0, 10.0, 0.2 );
    JSpinner spin1 = new JSpinner( model1 );

    f.add( spin1 );

    // Spinner with a SpinnerListModel filled with comedians

    String[] comedians = {
      "Joseph Hader", "Charlie Chaplin", "Vicco von Bülow",
      "Heinz Erhardt", "Michael Mittermeier" };

    SpinnerListModel model2 = new SpinnerListModel( comedians );
    JSpinner spin2 = new JSpinner( model2 );

    f.add( spin2 );

    f.pack();
    f.setVisible( true );
### 9.16.4 Datumsauswahl * Einen Dialog oder eine Komponente zur Auswahl eines Datums liefert Java nicht. Auf dem freien Markt gibt es allerdings einige Komponenten, die so etwas nachliefern. Unter ihnen sind: JXDatePicker vom SwingX-Projekt (https://swingx.dev.java.net/) zeigt Wochen eines Monats an. Eine Vorstellung gibt die Seite http://www.javalobby.org/java/forums/t45447.html. DateComboBox von Rob MacGrogan bietet eine Combo-Box für Datumseingaben. Die freie Bibliothek unter http://pswing.sourceforge.net/ blendet neben einem Textfeld eine Schaltfläche zur Datumsauswahl an. ### Ihr Kommentar Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück | <top> | vor >>
Zum Katalog
Java 7 – Mehr als eine Insel Jetzt bestellen
Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen? Ihre Meinung
Buchempfehlungen
| Java und XML
| Einstieg in Eclipse 3.7
| Android 3
| NetBeans Platform 7
| Java ist auch eine Insel
| Apps entwickeln für Android 4
| Java 7
Shopping
Versandkostenfrei bestellen in Deutschland und Österreich Info

Copyright © Galileo Press 2012
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

[Galileo Computing]
Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de