Nachdem in diesem Kapitel die besonderen Eigenschaften der Swing-Komponenten erläutert wurden, werden im nun folgenden Teil die unterschiedlichen Swing-Komponenten erklärt. Swing bietet ebenso wie das AWT eine Menge von Klassen, mit deren Hilfe grafische Benutzeroberflächen, einschließlich der Interaktionsmöglichkeiten, die sich aus der Verwendung der Maus und der Tastatur ergeben, erstellt werden können. Die Klassen, aus denen Swing besteht, sind Teil des Packages javax.swing. Werden daher Benutzerschnittstellen in Swing implementiert, so darf das Importieren dieses Packages nicht vergessen werden. import javax.swing.*; Wie in Kapitel 4 (Java-GUIs) beschrieben wurde, bestehen die statischen Komponenten einer Benutzerschnittstelle aus den folgenden drei Elementen: Komponenten : Komponenten sind die Bausteine, aus denen eine Benutzerschnittstelle besteht, bspw. Buttons, Textfelder, Grafiken oder Bilder. Container: Container werden dazu verwendet, Komponenten zu gruppieren. Ein Beispiel hierfür ist das JApplet-Fenster, das die Möglichkeit bietet, die darin enthaltenen Bestandteile als Gruppe aufzufassen. Layout-Manager: Zu den Layout-Managern, die in Kapitel 4.4 beschrieben wurden, kommt in Swing der BoxLayout-Manager hinzu. Die Funktionsweise dieses Layout-Managers wird im weiteren Verlauf dieses Kapitels beschrieben. Komponenten und Container Swing-Komponenten können in folgende Gruppen kategorisiert werden: Top-Level-Container : Diese Komponenten bilden die oberste Hierarchie einer Swing-Anwendung. Zu den Top-Level-Komponenten zählen JApplet, JDialog und JFrame. Universelle Container: Universelle Komponenten, die allgemein verwendet werden können. Hierzu gehören die Klassen JPanel, JScrollPane, JSplitPane und JTabbedPane. Spezielle Container: Interne Komponenten, die spezifische Aufgaben in der Benutzeroberfläche wahrnehmen. JInternalFrame, JLayeredPane, JRootPane und JToolBar sind Beispiele für derartige Komponenten. Grundlegende Kontrolle: Basiskomponenten, mit denen eine Auswahl erfolgen kann. JButton, JComboBox, JList, JMenu, JSlider und JTextField gehören zu dieser Kategorie. (Nicht editierbare) Informationsdarstellung: Zu dieser Klasse gehören Komponenten, deren Zweck die Informationsdarstellung in der Benutzeroberfläche ist. Zu dieser Gruppe zählen JLabel, JProgressBar und JToolTip. (Editierbare) Informationsdarstellung: Zu dieser Klasse gehören Komponenten, die dazu dienen, Informationen an die Benutzeroberfläche in einem bestimmten Format weiterzugeben. Der Inhalt dieser Komponenten kann editiert werden. JColorChooser, JFileChooser, JTable, JText und JTree gehören zu dieser Klasse. Abb. 8.9: Swing-Komponenten und die entsprechenden AWT-Komponenten Abb. 8-9 veranschaulicht die Gliederung von Swing-Komponenten, die eine Entsprechung im AWT finden. In Abb. 8-10 wird die Hierarchie der Lightweight-Komponenten in Swing darstellt, in Abb. 8-11 die der Heavyweight-Komponenten. Abb. 8.10: Klassenhierarchie der Lightweight-Komponenten in Swing Die Basisklasse javax.swing.JComponent vererbt die folgenden Eigenschaften an ihre Unterklassen: Ein Pluggable Look and Feel (PL&F), das sowohl zur Entwicklungszeit als auch während der Laufzeit geändert werden kann. Eine Menge an kombinierbaren Komponenten, die zusammengestellt oder erweitert werden können, um neue Komponenten zu entwickeln. Eine Möglichkeit, Tastenkombinationen (Mnemoniks) zu definieren, um die verschiedenen Komponenten zu steuern. Die Möglichkeit, die gewünschte Größe bzw. die minimale oder maximale Größe für eine Komponente zu setzen. Die Beschreibung der Aktionen beliebiger Komponenten durch ToolTips. Autoscrolling, das eine automatische Scrolling-Funktion in einer Liste oder Tabelle auslöst, wenn der Benutzer die Maus bewegt. Die Klasse JOptionPane erlaubt das Erzeugen von Standarddialogen durch das Aufrufen von statischen Methoden. Unterstützung der Accessibility. Unterstützung der Internationalisierung. Abb. 8.11: Klassenhierarchie der Heavyweight-Komponenten in Swing Vergleicht man die Abbildungen 8-9 , 8-10 und 8-11 miteinander, so stellt man fest, dass es Swing-Komponenten gibt, die keine entsprechenden Gegenstücke im AWT besitzen. Diese in Swing neu eingeführten Komponenten sind in Abb. 8-12 dargestellt. Im Folgenden werden die Komponenten, die Swing bereitstellt, erläutert. Klasse JRootPane Die Klasse JRootPane stellt das Basiselement der Heavyweight-Container JFrame, JDialog, JWindow, JApplet bzw. des einzigen Lightweight-Containers JInternal-Frame dar. JRootPane setzt sich aus zwei Objekten zusammen, JGlassPane und JLayeredPane. Das JGlassPane-Objekt ist unsichtbar und dient dazu, jedes Objekt, das in ihm platziert wird, im Vordergrund anzuzeigen. JLayeredPane besteht aus zwei Objekten, einer MenuBar und einer contentPane. Ein contentPane-Objekt verhält sich wie eine reguläre AWT-Komponente. Das Hinzufügen der Komponenten zu einem der Container, der im Package javax.swing definiert ist, erfolgt anders als bei AWT-Komponenten, in denen direkt die add-Methode aufgerufen wird. Eine Komponente in Swing muss stets in den Inhaltsbereich der übergeordneten Swing-Komponente eingefügt werden. Abb. 8.12: Swing-Komponenten, die keine Analogie zu AWT-Komponenten besitzen Die Syntax für das Setzen eines Layouts und für das Einfügen einer Komponente in einen AWT-Container lautet (siehe auch Kapitel 4 zu GUIs): AWTKomponentenTyp.setLayout(layout); AWTKomponentenTyp.add(eineKomponente); Bei der Benutzung der Swing-Komponenten hingegen wird das Setzen eines Layouts und das Einfügen einer Komponente dem Inhaltsbereich der Komponente überlassen: SwingKomponentenTyp.getContentPane().setLayout(layout); SwingKomponentenTyp.getContentPane().add(eineKomponente); Die wichtigsten Methoden von Swing-Containern sind: Container getContentPane() gibt den Inhaltsbereich eines Containers zurück. setContentPane (Container) setzt die gewünschte Komponente im Inhaltsbereich des Containers. Component getGlassPane() gibt den unsichtbaren Bereich eines Containers zurück. setGlassPane (Component) setzt die gewünschte Komponente im unsichtbaren Bereich des Containers. JLayeredPane getLayeredPane() gibt den überlagerten Bereich eines Containers zurück. setLayeredPane (JLayeredPane) setzt den überlagerten Bereich eines Containers. JMenuBar getMenuBar() gibt die Menüleiste eines Containers zurück. setMenuBar (JMenuBar) setzt die Menüleiste eines Containers. Klasse JLayeredPane Ein JLayeredPane-Objekt erlaubt es, Swing-Komponenten in verschiedene Schichten einzuordnen. Ein Integer-Objekt legt die Tiefe einer Komponente im Container fest, wobei eine höhere Zahl eine höhere Schicht repräsentiert. Die Verwendung von JLayeredPane garantiert eine wohldefinierte Überlappung verschiedener Swing-Komponenten. Standardmäßig sind folgende Schichten definiert: DEFAULT_LAYER Die Standardebene, in der die meisten Komponenten platziert werden. Diese Ebene stellt die unterste Schicht dar. PALETTE_LAYER Ist über dem DEFAULT_LAYER angeordnet und dient dazu, Toolbars zu platzieren, die dann über den normalen Komponenten erscheinen. MODAL_LAYER Wird für Dialogfenster benutzt, die die Bestätigung eines Benutzers erfordern. POPUP_LAYER Diese Schicht ist oberhalb von Dialogen angeordnet. Somit können Pop-Up-Fenster, ToolTips und andere Hilfsfunktionen im Vordergrund aller Komponenten erscheinen. DRAG_LAYER Wird bei der Benutzung von Drag and Drop verwendet. JLayeredPane stellt weiterhin Methoden zum Verschieben einer Komponente bzw. einer Schicht nach vorn (moveToFront(Component)) oder nach hinten (moveToBack(Component)) in der Schichtenhierarchie zur Verfügung. Die Position einer Komponente innerhalb einer Schicht kann auch direkt angegeben werden, indem die Methode setPosition angewendet wird. Die setLayer()-Methode wird dazu benutzt, um die momentane Schicht einer Komponente zu verändern. Klasse JApplet Da ein JApplet-Objekt gleichzeitig ein Heavyweight-Container ist, ist die direkte Verwendung der add-Methode innerhalb von JApplet-Objekten nicht zulässig. JApplet ist, wie in Abb. 8-11 angegeben, eine Erweiterung des Packages java.applet.Applet. Trotzdem weist die JApplet-Klasse eine geringfügige Inkompatibilität zu ihrer Elternklasse java.applet.Applet auf, da JApplet eine JRootPane-Klasse beinhaltet. Der Inhalt (child) dieser Klasse wird zur Vererbung auf jede Komponentenklasse verwendet, die eingefügt werden soll. Dies steht im Gegensatz zur Klasse java.applet.Applet. Abb. 8.13: Die Überlappung der Standard-JLayeredPane-Schichten Das Hinzufügen einer beliebigen Komponente in einem java.applet.Applet erfolgt nach folgender Syntax: applet.add(komponente); Im Gegensatz dazu kann eine Komponente nicht direkt in ein JApplet-Objekt eingefügt, sondern lediglich zu dessen Inhalt hinzugefügt werden, wozu die folgende Syntax zu verwenden ist: applet.getContentPane().add(komponente); Das gleiche gilt auch für das Setzen eines Layout-Managers und das Entfernen einer Komponente mittels der remove-Methode. Standard-Layout eines contentPane()-Objekts ist das BorderLayout. Klasse JFrame JFrame stellt eine Erweiterung der Klasse java.awt.Frame dar. Das Hinzufügen von Frames zu Applets muss im AWT mit der anschließend dargestellten Syntax erfolgen: frame.add(komponente); Ähnlich wie bei JApplet werden Komponenten nicht direkt hinzugefügt oder entfernt, sondern über ein contentPane-Objekt mittels folgender Syntax platziert: frame.getContentPane().add(komponente); JFrame unterscheidet sich von der Elternklasse java.awt.Frame, da das Schließen eines JFrame-Objekts unterschiedlich definiert ist. Die Methode setDefaultCloseOperation() erlaubt die Definition einer Funktion, die ein Frame-Objekt schließt, und somit das Überschreiben des Standardverhaltens. Das Aufrufen der Methode setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLO SE) erlaubt es bspw., dass sich ein JFrame-Objekt auf dieselbe Weise verhält wie ein java.awt.Frame-Objekt. Die Klasse JFrame kann ebenso wie die Elternklasse Frame mit zwei Konstruktoren aufgerufen werden: JFrame() erzeugt ein Fenster ohne Titel, JFrame(String) erzeugt ein Fenster mit dem Titel, der als Parameter übergeben wird. Weiterhin stellt JFrame die folgenden Hilfsmethoden zur Verfügung (die Methoden der Elternklasse Frame sind in Kapitel 4.4 beschrieben): Container getContentPane() und setContentPane(Container) erfragen das contentPane-Objekt dieser JFrame-Klasse bzw. setzen dieses. int getDefaultCloseOperation() und setDefaultCloseOperation(int) erfragen die Operation, die beim Schließen des JFrame ausgeführt werden bzw. setzen diese. JMenuBar getJMenuBar() und setJMenuBar(JMenuBar) erfragen die Menubar dieses Frames bzw. setzen diese. void update(Graphics) ruft die paint-Methode auf. Das folgende Programmstück demonstriert das Öffnen eines Fensters aus einer Anwendung heraus. import java.awt.*; import javax.swing.*; public class JFenster extends JFrame {; public JFenster() { } public static void main(String[] args) { JFenster fenster = new JFenster(); fenster.setSize(100, 100); fenster.pack(); fenster.setVisible(true); } } Klasse JInternalFrame Mit JInternalFrame wird es möglich, innerhalb eines Panels oder eines Frames beliebige Frames zu definieren. Um JInternalFrame richtig zu verwenden, müssen die folgenden Regeln beachtet werden: JInternalFrame muss zu einem Container hinzugefügt werden. Geeigneterweise fügt man JInternalFrame zu einem JDesktopPane-Objekt hinzu, also zu einer Erweiterung des JLayeredPane. JInternalFrame ist wie ein JButton-Objekt automatisch sichtbar, wenn es erzeugt wird. Die Methode setVisible(boolean) sollte daher nicht explizit aufgerufen werden. Die Größe und die Bildschirmposition des JInternalFrame-Objekts müssen angegeben werden, damit es angezeigt wird. Folgende Methoden können verwendet werden, um die Größe eines JInternalFrame-Objekts zu setzen: setSize(), pack() oder setBounds(). Fehlt die Positionsangabe, so erscheint das JInternal-Frame-Objekt an der Position (0,0). Die Positionsangabe erfolgt durch die Methoden setLocation() oder setBounds(). Ein JInternalFrame-Objekt verhält sich bezüglich hinzukommenden Komponenten wie ein JFrame-Objekt. Eine Komponente kann einem JInternalFrame-Objekt nicht direkt hinzugefügt werden, sondern lediglich in dessen contentPane-Objekt. JInternalFrame generiert JInternalFrame-Events anstelle von Window-Events. Die Behandlung dieser Events ist allerdings identisch mit der Verarbeitung von Window-Events. Das folgende Beispiel verdeutlicht die Anwendung von JInternalFrame-Objekten. In diesem Beispiel wird ein JInternalFrame-Objekt wie folgt erzeugt: protected void createFrame() { JInternalFrame frame = new JInternalFrame("InternalFrame#" + (++offeneFrames) true, //in der Groesse anpassbar true, // Schliessbar true, // Vergroesserbar true); //ikonifizierbar frame.setLocation(20*offeneFrames,20*offeneFrames); frame.setSize(200,200); desktop.add(frame); try { frame.setSelected(true); } catch (java.beans.PropertyVetoException e2) {} } Der Konstruktor verwendet zusätzlich zum Titel des Frames noch vier boole'sche Variablen. Die erste Variable sagt aus, ob das JInternalFrame-Objekt in der Größe variierbar ist oder nicht. Die zweite boole'sche Variable ermöglicht es, das JInternalFrame-Objekt zu schließen. Die dritte Variable beinhaltet Informationen darüber, ob das JInternalFrame-Objekt vergrößerbar ist und die vierte Variable definiert die Möglichkeit, das JInternalFrame-Objekt zu ikonifizieren. Abb. 8.14: JInternalFrames Im Folgenden werden die wichtigsten der oben dargestellten Regeln angewendet, die Eingabe der Größe und der Position des JInternalFrame-Objekts und das Hinzufügen des JInternalFrame-Objekts zu einer Instanz des JDesktopPane-Objekts. Der Ausdruck zwischen den Anweisungen try und catch hat keine Auswirkung auf die Generierung eines JInternalFrame-Objekts. Er sagt lediglich aus, dass das erzeugte JInternalFrame-Objekts automatisch selektiert werden soll. In Abb. 8-14 ist das Resultat dargestellt, das sich nach fünfmaliger Instantiierung von JInternal-Frame mit dem Menüpunkt neu und nach der Ikonifizierung der Frames 2, 4 und 5 ergibt. Klasse AbstractButton Die Klasse AbstractButton stellt keine Komponente dar, die direkt anwendbar ist. AbstractButton ist eine abstrakte Klasse, die das allgemeine Verhalten der folgenden Unterklassen festlegt: JButton, JToggleButton, JCheckbox und JRadioButton. Die wichtigsten Methoden der AbstractButton-Klasse sind: doClick() : entspricht dem Drücken des Buttons. setIcon(Icon) und setMnemonic(): setzen das gewünschte Icon bzw. Mnemonik-Zeichen, um das Bedienen des Programms durch die Tastatur zu ermöglichen. setVerticalAlignment(int) und setHorizontalAlignement(int): bestimmen die relative horizontale bzw. vertikale Position des Icons zum Text. Klasse JButton JButton unterscheidet sich von der AWT-Klasse java.awt.Button durch die Möglichkeit, ein Icon hinzuzufügen. Ein JButton-Objekt kann mit den folgenden Konstruktoren erzeugt werden: JButton() erzeugt einen Knopf ohne Textmarke, die die Funktion des Buttons spezifiziert, JButton(String) erzeugt entsprechend einen Button mit Textmarke, JButton(Icon) erzeugt entsprechend einen Button mit Icon, JButton(String, Icon) erzeugt entsprechend einen Button mit Textmarke und Icon. Das folgende Beispiel, dessen Ausgabe in Abb. 8-15 dargestellt ist, zeigt die Verwendung dieser Klasse. Es muss allerdings darauf hingewiesen werden, dass wie bei der in java.awt.Button definierten Klasse die Verwendung von JButton dann relativ sinnlos ist, wenn mit einem Knopf keine Aktion assoziiert ist, die ausgelöst wird, wenn der Knopf gedrückt wird. Die hierzu notwendige Event-Verarbeitung wird an späterer Stelle dieses Kapitels erläutert. import java.awt.*; import javax.swing.*; public class JKnoepfe extends JApplet{ ImageIcon icon1 = new ImageIcon("left.gif"); JButton start = new JButton("Beginne Spiel"); JButton ende = new JButton("Beende Spiel", icon1); public void init () { this.getContentPane().add(start, BorderLayout.NORTH); this.getContentPane().add(ende, BorderLayout.SOUTH); } } Abb. 8.15: Buttons in Swing Klasse JLabel Die Klasse JLabel ähnelt stark der AWT-Klasse java.awt.Label, bietet aber die folgende zusätzliche Funktionalität: Hinzufügen eines Icons. Setzen der vertikalen und horizontalen Position des Textes bezüglich des Icons. Setzen der relativen Position des Inhalts innerhalb der Komponente. Zur Definition eines JLabel-Objekts können die folgenden Konstruktoren verwendet werden: JLabel() erzeugt eine leere Textmarke, in der der Text linksbündig angeordnet wird, JLabel(String) erzeugt eine Textmarke mit dem Text, der als Parameter übergeben wurde. Der Text wird ebenfalls linksbündig dargestellt. JLabel(Icon) erzeugt eine Textmarke mit dem Icon, der als Parameter übergeben wurde. Das Icon wird zentriert dargestellt. JLabel(String, int) erzeugt dieselbe Ausgabe wie JLabel(String), der zweite Parameter legt allerdings die Ausrichtung des Textes innerhalb der Textmarke fest. Hierzu können die Konstanten JLabel.LEFT, JLabel.CENTER und JLabel.RIGHT verwendet werden. JLabel(Icon, int) erzeugt dieselbe Ausgabe wie JLabel(Icon), der zweite Parameter legt allerdings die horizontale Ausrichtung des Icons innerhalb der Textmarke fest. Hierzu können die Konstanten JLabel.LEFT, JLabel.CENTER und JLabel.RIGHT verwendet werden. JLabel(String, Icon, int) ist eine Kombination von JLabel(String) und JLabel(Icon), der dritte Parameter legt die horizontale Ausrichtung des Textes und Bildes innerhalb der Textmarke fest. Hierzu können die Konstanten JLabel.LEFT, JLabel.CENTER und JLabel.RIGHT verwendet werden. Das Icon ist immer auf der rechten Seite des Textes angeordnet. Das folgende Beispiel stellt die Verwendung der Klasse JLabel dar. import java.awt.*; import javax.swing.*; public class JTextmarke extends JApplet{ ImageIcon icon1 = new ImageIcon("left.gif"); JLabel links = new JLabel("Schiffe versenken", icon1, JLabel.LEFT); JLabel zentriert = new JLabel("Schiffe versenken",icon1, JLabel.CENTER); JLabel rechts = new JLabel("Schiffe versenken", icon1,JLabel.RIGHT); public void init () { this.getContentPane().add(links, BorderLayout.NORTH); this.getContentPane().add(zentriert, BorderLayout.CENTER); this.getContentPane().add(rechts, BorderLayout.SOUTH); } } Abb. 8.16: Textmarken in Swing Die Ausgabe dieses Beispiels ist in Abb. 8-16 dargestellt. Wie man dieser Abbildung entnehmen kann, ist das Bild immer rechts vom Text platziert. Die Klassen JCheckbox, JRadioButton und JToggleButton JToggleButton ist die Superklasse von JCheckBox und JRadioButton. Diese Klassen werden dazu verwendet, um Optionen eines Programms auswählen zu können. Die Auswahl ist hierbei nicht exklusiv, es können also auch mehrere Optionen ausgewählt werden. Soll genau eine Option auswählbar sein, so wird die Klasse JRadiobutton verwendet, die im Folgenden erklärt wird. JToggleButton und die davon abgeleiteten Komponenten werden mit den folgenden Konstruktoren erzeugt: JToggleButton() oder JCheckBox() oder JRadioButton(), JToggleButton(Icon) oder JCheckBox(Icon) oder JRadioButton(Icon), JToggleButton(Icon, boolean) oder JCheckBox(Icon, boolean) oder JRadioButton(Icon, boolean), JToggleButton(String) oder JCheckBox(String) oder JRadioButton(String), JToggleButton(String, boolean) oder JCheckBox() oder JRadioButton(), JToggleButton(String, Icon) oder JCheckBox(String, Icon) oder JRadioButton(String, Icon), JToggleButton(String, Icon, boolean) oder JCheckBox(String, Icon, boolean) oder JRadioButton(String, Icon, boolean). Die Verwendung dieser Konstruktoren entspricht exakt der Benutzung der Konstruktoren von JLabel-Objekten. Der Anfangszustand der so erzeugten Boxen ist immer die leere Box. Dieser Zustand kann aber mit der Methode setSelected(boolean) verändert werden. Wird der Wert auf true gesetzt, so ist die Box mit einem Haken versehen, anderenfalls ist die Box leer. Die Methode zur Abfrage des Zustands einer Checkbox lautet isSelected(). Das folgende Beispiel, dessen Ausgabe in Abb. 8-17 dargestellt ist, verdeutlicht die Verwendung von Checkboxen. import java.awt.*; import javax.swing.*; public class JCheckboxen extends JApplet{ JCheckbox audio1 = new JCheckbox("Ton ausschalten", true); JCheckbox audio2 = new JCheckbox("Ton einschalten", false); public void init () { this.getContentPane().add(audio1, BorderLayout.NORTH); this.getContentPane().add(audio2, BorderLayout.CENTER); } } Abb. 8.17: Checkboxen in Swing Klasse JTextComponents JTextComponent ist eine allgemeine Textklasse, die Teil des Packages javax.swing.text ist. JTextComponent bietet alle Merkmale eines einfachen Editors an. Die wichtigsten Methoden dieser Klasse sind: copy() zum Kopieren von Text, cut() zum Ausschneiden von Text, paste() zum Einfügen von Text, getSelectedText() zur Rückgabe von selektiertem Text, setSelectionStart() setzt den Anfang des zu selektierenden Textes, setSelectionEnd() setzt das Ende des selektierten Textes, selectAll() selektiert den gesamten Inhalt, replaceSelection() überschreibt den selektierten Bereich, getText() gibt den Text zurück, setText() setzt den Text, setEditable() bestimmt, ob der Inhalt editierbar ist oder nicht, setCaretPosition() setzt die Position des Einschaltungszeichens. Die folgenden drei Swing-Komponenten erweitern diese Klasse: JTextField, JTextArea und JTextPane. Klassen JTextField und JTextArea JTextField und JTextArea unterscheiden sich von den entsprechenden AWT-Komponenten java.awt.TextField und java.awt.TextArea nicht. Die Instantiierung eines JTextField-Objekts erfolgt mittels der Anweisung: JTextField jtf = new JTextField(); Die Erzeugung eines neuen JTextArea-Objekts erfolgt durch: JTextArea jta = new JTextArea(); Beide Klasse werden durch die Methode setText() initialisiert, wobei JTextArea mehrfache Zeilenangaben erlaubt: jtf.setText("TextField"); jta.setText("JTextArea\n erlaubt mehrfache Zeilen"); add(jtf); add(jta); Klasse JTextPane JTextPane stellt einen Text-Editor dar, der formatierten Text, Zeilenumbrüche und die Anzeige von Bildern unterstützt. Klasse JPasswordField JPasswordField ist eine Erweiterung von JTextField, die benutzt werden kann, um den Inhalt eines JTextField-Objekts zu maskieren. Der Asteriskus ('*') wird hierbei zur standardmäßigen Maskierung des Inhaltes verwendet. Diese Maskierung kann mittels setEchoChar() vom Programmierer geändert werden. Das folgende Code-Segment erzeugt die in Abb. 8-18 dargestellten Eingabefelder für Passwörter: import javax.swing.*; import java.awt.*; import java.awt.event.*; class PasswordPanel extends JPanel { PasswordPanel() { //Das JPasswordField-Objekt pass1 hat die standardmaessige //Maskierung ('*') . Fuer das Objekt pass2 wurde explizit die //Maskierung ('?') gesetzt. Beide Objekte haben ein 20 //Zeichen langes Eingabefeld. JPasswordField pass1 = new JPasswordField(20); JPasswordField pass2 = new JPasswordField(20); pass2.setEchoChar ('?'); add(pass1); add(pass2); } public static void main(String[] args){ JFrame frame = new JFrame("Password Eingeben"); PasswordPanel pp = new PasswordPanel(); frame.getContentPane().add(pp); frame.pack(); frame.setVisible(true); } } Abb. 8.18: Anwendung der Klasse JPasswordField Klasse JComboBox JComboBox unterscheidet sich von der AWT-Komponente Choice dadurch, dass einige zusätzliche Methoden angeboten werden. JComboBox bietet die Möglichkeit, eine bestimmte Auswahlliste zu erweitern. Dies ist insbesondere dann nützlich, wenn das vorhandene Angebot nicht ausreichend ist. Der folgende Code erzeugt die in Abb. 8-19 dargestellten JComboPanel-Eingabefelder: import javax.swing.*; import java.awt.*; import java.awt.event.*; public class JComboPanel extends JPanel { String choices[] = {"Mercury", "Venus", "Earth", "Mars", "Saturn", "Jupiter", "Uranus","Neptune", "Pluto"}; public JComboPanel() { JComboBox combo1 = new JComboBox(); JComboBox combo2 = new JComboBox(); for (int i=0;i<choices.length;i++) { combo1.addItem (choices[i]); combo2.addItem (choices[i]); } combo2.setEditable(true); combo2.setSelectedItem("Bitte Wählen"); combo2.setMaximumRowCount(4); add(combo1); add(combo2); } public static void main(String[] args){ JFrame frame = new JFrame("Auswahl Liste"); JComboPanel jcp = new JComboPanel(); frame.getContentPane().add(jcp); frame.pack(); frame.setVisible(true); } } Abb. 8.19: Beispiel der Verwendung der Klasse JComboBox Klasse JList Die JList-Komponente unterscheidet sich von der AWT-Komponente java.awt.List dadurch, dass JList im Gegensatz zu java.awt.List keine direkte Verschiebungsfunktion anbietet. Aus programmiertechnischen Gründen muss dementsprechend JList folgendermaßen in einem ScrollPane-Objekt platziert werden: import javax.swing.*; import java.awt.*; import java.awt.event.*; public class JListPanel extends JPanel { String label [] = {"eins", "zwei", "drei", "vier", "fünf", "sechs","sieben", "acht", "neun", "zehn" }; public JListPanel() { setLayout (new BorderLayout()); JList list = new JList(label); ScrollPane pane = new ScrollPane(); pane.add (list); add(pane, BorderLayout.CENTER); } public static void main(String[] args){ JFrame frame = new JFrame("JListPanel"); JListPanel jlp = new JListPanel(); frame.getContentPane().add(jlp); frame.pack(); frame.setVisible(true); } } Abb. 8.20: Verwendung der Klasse JList in einem ScrollPane-Objekt Klasse Border Das Package javax.swing.border beinhaltet verschiedene Objekte, um Begrenzungen um Swing-Komponenten herum zu erstellen. Die verschiedenen Border-Stile implementieren das Interface javax.swing.border.Border, das die folgenden drei Methoden beinhaltet: public Insets getBorderInsets(Component) : definiert den nötigen Bereich, um den Rand (Border) einer Komponente zu zeichnen. public boolean isBorderOpaque(): legt fest, ob der umrandete Bereich durchsichtig ist oder nicht. public void paintBorder (Component c, Graphics g, int x, int y, int width, int height): zeichnet einen Rand um den festgelegten Bereich. Das Package javax.swing.border bietet neun Begrenzungsstile an: BevelBorder : setzt einen 3-D-Rand, der aufgehoben oder abgesetzt sein kann. CompoundBorder: setzt einen Rand, der mehrere Ränder verschachtelt enthalten kann. DefaultBorder: stellt eine Klasse dar, die das Border-Interface mit Standardwerten implementiert. EmptyBorder: reserviert Platz für einen leeren Rand. EtchedBorder: setzt einen Rand, der weder aufgehoben noch abgesetzt wird. LineBorder: setzt einen einfarbigen Rand beliebiger Dicke. MatteBorder: setzt einen Rand, der das Überdecken eines Icons oder einer Farbe erlaubt. SoftBevelBorder: setzt einen 3-D-Rand mit weichen Ecken. TitledBorder: setzt einen Rand, der das Setzen einer Überschrift erlaubt. Der Rand eines beliebigen JComponent-Objekts kann mittels der Methode setBorder() geändert werden. Das folgende Beispiel, dessen Ausgabe in Abb. 8-21 dargestellt ist, verdeutlicht die Verwendung der oben definierten Randstile: import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; public class BorderPanel extends JPanel { public BorderPanel() { setLayout (new GridLayout (4, 3, 5, 5)); //Verwendung eines leeren Randes JButton b = new JButton("Empty"); b.setBackground (SystemColor.control); b.setBorder (new EmptyBorder (1,1,1,1)); add(b); //Verwendung eines Etched-Randes b = new JButton ("Etched"); b.setBackground (SystemColor.control); b.setBorder (new EtchedBorder ()); add(b); //Verwendung eines gefaerbten Etched-Randes b = new JButton ("ColorizedEtched"); b.setBackground (SystemColor.control); b.setBorder (new EtchedBorder (Color.red, Color.green)); add(b); //Verwendung eines Rands mit Ueberschrift b = new JButton ("Titled/Line"); b.setBackground (SystemColor.control); b.setBorder(new TitledBorder (new TitledBorder(LineBorder.createGrayLineBorder(),"S chiffe"), "Versenken", TitledBorder.RIGHT, TitledBorder.BOTTOM)); add(b); //Verwendung eines aufgehobenen Randes b = new JButton ("Bevel Up"); b.setBackground (SystemColor.control); b.setBorder(new BevelBorder(BevelBorder.RAISED)); add(b); //Verwendung eines abgesetzten Randes b = new JButton ("Bevel Down"); b.setBackground (SystemColor.control); b.setBorder(new BevelBorder(BevelBorder.LOWERED)); add(b); //Verwendung eines 3-D-Randes mit weichen Ecken b = new JButton ("Soft Bevel"); b.setBackground (SystemColor.control); b.setBorder(new SoftBevelBorder(SoftBevelBorder.LOWERED)); add(b); //Verwendung eines Randes, der das Ueberdecken einer Farbe erlaubt b = new JButton ("Matte"); b.setBackground (SystemColor.control); b.setBorder(new MatteBorder(5, 10, 5, 10, Color.red)); add(b); //Verwendung eines Randes, der das Ueberdecken eines Icons erlaubt b = new JButton ("Matte Icon"); b.setBackground (SystemColor.control); Icon icon = new ImageIcon ("face1.gif"); b.setBorder(new MatteBorder(10, 10, 10, 10, icon)); add(b); //Verwendung eines 3-D-Randes, der aufgehoben ist b = new JButton ("ColorizedBezel"); b.setBackground (SystemColor.control); b.setBorder(new BevelBorder(BevelBorder.RAISED, Color.red, Color.pink)); add(b); } public static void main(String[] args){ JFrame frame = new JFrame("JListPanel"); BorderPanel bp = new BorderPanel(); frame.getContentPane().add(bp); frame.pack(); frame.setVisible(true); } } Abb. 8.21: Verwendung der in javax.swing.border definierten Randstile Klasse JTabbedPane JTabbedPane bietet die Möglichkeit, mehrere Panels in eine Kartei einzuordnen und damit den Zugriff auf diese Panels zu erleichtern. Eine Karte, eine sog. Card, wird einer Kartei durch die Methode addTab hinzugefügt. Diese Methode kann auf drei verschiedene Weisen aufgerufen werden: addTab (String title, Component component) : erzeugt ein neues Tab-Panel mit den Parametern Titel und Komponente. addTab (String title, Icon icon, Component component): fügt dem Titel ein Icon hinzu. addTab (String title, Icon icon, Component component, String tip): fügt zusätzlich einen ToolTip-Text hinzu. Das folgende Beispiel, dessen Ausgabe in Abb. 8-22 dargestellt ist, verdeutlicht die Benutzung von JTabbedPane . public class JTabbedPanel extends JPanel { //Titel für die zu erstellenden Tabs String tabs[] = {"Open", "Java", "Schiffe", "Versenken"}; public JTabbedPane tabbedPane = new JTabbedPane(); Durch die oben angegebenen Zeilen werden vier Tabs erstellt. Zu jedem dieser Tabs werden ein Icon (Null-Objekt) und ein Panel, das durch createPane erstellt wird, hinzugefügt. public JTabbedPanel() { setLayout (new BorderLayout()); for (int i=0;i<tabs.length;i++) tabbedPane.addTab (tabs[i], null, createPane (tabs[i])); tabbedPane.setSelectedIndex(2); add (tabbedPane, BorderLayout.CENTER); } createPane erstellt ein JPanel und fügt diesem ein JLabel zu, das den Titel angibt. JPanel createPane(String s) { JPanel p = new JPanel(); p.setBackground (SystemColor.control); p.add(new JLabel(s)); return p; } public static void main(String[] args){ JFrame frame = new JFrame("JTabbed Panel"); JTabbedPanel jtp = new JTabbedPanel(); frame.getContentPane().add(jtp); frame.pack(); frame.setVisible(true); } } Abb. 8.22: Anwendung der Klasse JTabbedPanel Klasse JSplitPane JSplitPane unterteilt eine Komponente in zwei weitere Komponenten. Die Größe jeder dieser Komponenten wird vom Benutzer interaktiv angepasst. Die zwei Komponenten können sowohl horizontal mit JSplitPane.HORIZONTAL_SPLIT als auch vertikal mit JSplitPane.VERTICAL_SPLIT ausgerichtet werden. JSplitPane kann mit anderen JSplitPane-Komponenten verschachtelt werden, bis das gewünschte Aussehen erreicht wird. Die Methode setContinuousLayout(boolean) veranlasst, dass jedes Teil-Panel aktualisiert wird, wenn die Größe eines Teils geändert wird. Das folgende Beispiel definiert zwei JSplitPane-Objekte und drei JButton-Objekte: private JSplitPane splitPane, splitPaneBottom; private JButton topButton = new JButton("Top"); private JButton rightButton = new JButton("BottomRight"); private JButton leftButton = new JButton("BottomLeft"); splitPane wird in horizontaler, splitPaneBottom in vertikaler Richtung unterteilt. Diese Unterteilung wird durch den folgenden Code erreicht: splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); splitPaneBottom = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); Dem oberen Teil von splitPane wird anschließend das Objekt topButton hinzugefügt. In splitPaneBottom werden jeweils links und rechts leftButton und rightButton hinzugefügt. Anschließend wird dieses Panel dem unteren Teil von splitPane hinzugefügt. splitPane.setTopComponent(topButton); splitPaneBottom.setRightComponent(rightButton); splitPaneBottom.setLeftComponent(leftButton); splitPane.setBottomComponent(splitPaneBottom); Durch die folgenden Methoden wird das Expandieren der einzelnen Panel-Objekte mit einem Mausklick auf das Dreieck (siehe Abb. 8-23) gewährleistet. splitPane.setOneTouchExpandable(true); splitPaneBottom.setOneTouchExpandable(true); Abb. 8.23: Anwendung der Klasse JSplitPane Klasse JTable JTable unterstützt die Anordnung von Komponenten in Tabellenform. JTable ist derart eine GUI-Komponente, anhand derer Daten im zweidimensionalen Tabellenformat präsentiert werden können. JTable erlaubt dem Benutzer auf einfache Art und Weise, Daten zu editieren bzw. Daten abzufragen. Die Spalten und Zeilen einer derartigen Tabelle werden durch ganzzahlige Werte adressiert. Durch die Methode getValueAt(int, int) wird der Wert einer Zelle abgefragt und in der Tabellenansicht dargestellt. JTable unterstützt Scroll-Objekte nicht direkt, weshalb es empfehlenswert ist, ein JTable-Objekt einem JScrollPane-Objekt hinzuzufügen. JTable verfügt u. a. über die folgenden Konstruktoren: JTable(Object[][] rowData, Object[] columnNames) JTable(Vector rowData, Vector columnNames) Um deren Funktionsweise zu verdeutlichen, wird die folgende Datenstruktur definiert: Object[][] daten = { {"Julia","Java",new Integer(25),"Squatch",new Boolean(false)}, {"Abed", "Mustermann", new Integer(29),"Soccer", new Boolean(true)}, {"stefi", "Walrath", new Integer(29), "Soccer",new Boolean(true)}, {"Mark", "Andrews", new Integer(20), "Soccer",new Boolean(true)}, }; String[] columnNames = {"Name","Vorname","Alter","Sport", "Maennlich"}; Somit kann die Tabelle wie folgt instantiiert werden: JTable tabelle = new JTable(daten, columnNames); Nun soll diese Tabelle einem JScrollPane-Objekt hinzugefügt werden. Dies geschieht mit Hilfe folgender Programmzeile: JScrollPane scrollPane = new JScrollPane(tabelle); JScrollPane erkennt automatisch die Namen der Spalten und platziert sie an der geeigneten Stelle. Alle Spalten einer JTable weisen die gleiche Breite auf. Wenn eine bestimmte Spalte eine andere Größe haben soll, so muss dies explizit angegeben werden. Das folgende Code-Fragment bewirkt, dass die dritte Spalte kleiner als die anderen erscheint: TableColumn column = null; for (int i = 0; i < 5; i++) { column = table.getColumnModel().getColumn(i); if (i == 2) { column.setPreferredWidth(50); //Dritte Spalte ist schmaler } else { column.setPreferredWidth(100); } } Im Folgenden wird ein Datenmodell für die Tabelle erstellt: class meinDatenModell extends AbstractTableModel() { final String[] columnNames = ...//von oben... final Object[][] daten = ...//von oben... public String getColumnName(int col) { return columnNames[col].toString(); } public int getRowCount() { return rowData.length; } public int getColumnCount() { return columnNames.length; } public Object getValueAt(int row, int col) { return rowData[row][col]; } public boolean isCellEditable(int row, int col) { return true; } public void setValueAt(Object value, int row, int col) { rowData[row][col] = value; fireTableCellUpdated(row, col); } } Um Änderungen der Zellen zu erkennen, wird das Interface TableModelListener implementiert: class TML implements TableModelListener { public void tableChanged(TableModelEvent e) { for(int i = 0; i < data.length; i++) { for(int j = 0; j < data[0].length; j++) System.out.print(data[i][j] + " "); System.out.println(); } } } Anschließend wird ein Listener hinzugefügt (zur Event-Verarbeitung siehe das nachfolgende Teilkapitel). meinDatenModell() { addTableModelListener(new TML()); } Sollen die Werte der vierten Spalte aus einer JComboBox gewählt werden, so wird folgender Code benötigt: TableColumn sportColumn = table.getColumnModel().getColumn(3); JComboBox comboBox = new JComboBox(); comboBox.addItem("Snowboarding"); comboBox.addItem("Rowing"); comboBox.addItem("Chasing toddlers"); comboBox.addItem("Speed reading"); comboBox.addItem("Teaching high school"); comboBox.addItem("None"); sportColumn.setCellEditor(new DefaultCellEditor(comboBox)); Das Resultat dieser Anwendung ist in Abb. 8-24 dargestellt. Abb. 8.24: Ein einfaches JTable-Beispiel Klasse JTree Die Klasse JTree dient der Darstellung eines hierarchischen Baums von Elementen. JTree ist eine Komponente, mit der bestimmte Informationen (Knoten und Blätter) am Bildschirm in einer Baumstruktur sichtbar gemacht werden können. Die in JTree dargestellten Informationen (Modell) werden mit einem sog. TreeModel beschrieben, das aus TreeNodes besteht. Für die meisten Anwendungsfälle werden die Klassen DefaultTreeModel und DefaultMutableTreeNode verwendet. Die Darstellung von TreeNodes in JTree (also in der View) erfolgt mit einem TreeCellRenderer. Die Steuerung der Aktionen, die der Benutzer mit einem JTree durchführt (also der Controller), bspw. durch Auswahl oder durch Doppelklick auf Elemente, erfolgt mit dem in diesen Klassen vorgesehenen Event-Handling. Weiterhin generieren die Methoden von TreeModel eigene Events, die dafür sorgen, dass sich die Darstellung im JTree ändert, wenn sich die Daten in TreeModel ändern. Das folgende Beispiel, dessen Ausgabe in Abb. 8-25 dargestellt ist, verdeutlicht das Arbeiten mit der Klasse JTree: //Die Klasse Branch akzeptiert einen Array und //erzeugt aus dem ersten Element einen Knoten, die anderen //Elemente werden zu Blaettern class Branch { DefaultMutableTreeNode r; public Branch(String[] data) { r = new DefaultMutableTreeNode(data[0]); for(int i = 1; i < data.length; i++) r.add(new DefaultMutableTreeNode(data[i])); } public DefaultMutableTreeNode node() { return r; } } public class JTrees extends JPanel { //Definition der Elemente von JTree String[][] data = { { "Farbe", "Rot", "Gelb", "Grün" },{ "Länge", "Klein", "Mittel", "Gross" }, { "Volume", "Viel", "Mittel", "wenig" }, { "Temperatur", "Viel", "Mittel", "wenig" }, { "Farbe2", "Lila", "Blau", "Schwarz" }, }; static int i = 0; DefaultMutableTreeNode root, child, chosen; JTree tree; DefaultTreeModel model; public JTrees() { setLayout(new BorderLayout()); root = new DefaultMutableTreeNode("root"); tree = new JTree(root); Ein Jtree-Objekt besitzt nicht automatisch eine ScrollBar-Funktion, weshalb JTree einem JScrollBar-Objekt hinzugefügt werden muss. add(new JScrollPane(tree),BorderLayout.CENTER); //Definition eines JTree-Modells und eines JButtons, damit //JTree aufgeklappt werden kann model =(DefaultTreeModel)tree.getModel(); JButton test = new JButton("Press me"); Eine innere ActionListener-Klasse wird mit dem folgenden Code erstellt: test.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ if(i < data.length) { child = new Branch(data[i++]).node(); // Wurde bereits geklickt? chosen = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if(chosen == null) chosen = root; // tree update model.insertNodeInto(child, chosen, 0); } } }); Nachdem die Vorder- und Hintergrundfarbe des JButton-Objekts festgelegt wurden, wird dieses in ein JPanel eingefügt. test.setBackground(Color.blue); test.setForeground(Color.white); JPanel p = new JPanel(); p.add(test); add(p, BorderLayout.SOUTH); } public static void main(String args[]) { JFrame frame = new JFrame("JTree in Aktion"); JTrees jtr = new JTrees(); frame.getContentPane().add(jtr); frame.pack(); frame.setVisible(true); } } Abb. 8.25: JTree-Objekt in Swing