FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 500 – 00 – TH – 01 ----------------------------------------------------------------------------------- Programmieren in Java Kapitel 5 5. Graphische Benutzeroberflächen 5.1 Grundprinzip 5.2 Java Foundation Classes – Überblick 5.3 Basisklassen der JFC-Hierarchie 5.4 Erstellung einer GUI-Anwendung 5.5 Beeinflussung des Erscheinungsbildes 5.6 Ausgewählte Swing-Komponenten-Klassen 5.7 Ereignisverarbeitung 5.8 Entwurfsmuster mit GUI-Bezug Stand 05.10.2013 1 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 511 – 00 – TH – 02 ----------------------------------------------------------------------------------- Grundprinzip graphischer Benutzeroberflächen Aufbau graphischer Benutzeroberflächen ◇ Eine graphische Benutzeroberfläche (Graphical User Interface, GUI) ist typischerweise aus hierarchisch strukturierten Komponenten aufgebaut. ◇ Die einzelnen GUI-Komponenten – auch controls oder widgets ( = window gadgets) genannt – erfüllen jeweils spezielle Aufgaben. Die meisten von ihnen ermöglichen es dem Benutzer, durch Eingaben mittels Maus, Tastatur oder einem anderen Eingabegerät mit Ihnen und damit mit dem Programm zu interagieren. ◇ Die hierarchische Struktur einer graphischen Benutzeroberfläche ergibt sich dadurch, dass einige GUI-Komponenten, Container genannt, andere Komponenten enthalten können. An der "Spitze" jeder graphischen Benutzeroberfläche steht ein sogenannter Top-Level-Container. Die in ihm enthaltenen Komponenten können "elementare" Komponenten oder wiederum Container sein, welche dann weitere Komponenten enthalten können, usw. Eine an einen Container gerichtete Aufgabe (z.B. paint(), "zeichne Dich") führt dieser für sich – soweit zutreffend – aus und delegiert dann die weitere Ausführung an alle in ihm enthaltenen Komponenten. Eine derartige Struktur führt programmtechnisch zu einer Implementierung des Entwurfsmusters Composite. ◇ Beispiele für elementare GUI-Komponenten : ▪ Beschriftung (Label) ▪ Textfeld (Text Field) ▪ Schaltfläche (Button) ▪ Auswahlfeld (Check Box) ▪ Auswahlliste (List, Combo Box) ▪ Menue (Menu) ◇ Beispiele für GUI-Container ▪ Fenster mit Rahmen (Frame) ▪ Dialogbox (Dialog Box) ▪ Menueleiste (Menu Bar) ▪ Werkzeugleiste (Tool Bar) ▪ Gruppierungsfeld (Panel) ◇ Beispiel : Rahmenfenster (Frame) mit Schaltfläche (Button) und Beschriftung (Label) Interaktion mit dem Benutzer ◇ Die Interaktion mit dem Benutzer erfolgt ereignisgesteuert. Jede auf eine GUI-Komponente fallende Benutzereingabe (Mausklick, Mausbewegung, Tastatureingabe, Auswahl aus einem Menu usw) wird als Ereignis bezeichnet. ◇ Für jede Komponente kann festgelegt werden, auf welches Ereignis wie reagiert werden soll. ◇ Beim Eintritt eines Ereignisses wird die für die betreffende Komponente registrierte Reaktion ausgeführt. 2 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 521 – 00 – TH – 03 ----------------------------------------------------------------------------------- Java Foundation Classes – Überblick (1) Bestandteile der Java Foundation Classes (JFC) ◇ Mit den JFC stellt Java eine breites Spektrum von Bibliotheks-Klassen zur Realisierung graphischer Funktionalität und zum Aufbau von GUIs zur Verfügung. ◇ Die für die Anwendung in GUIs wichtigsten Klassen lassen sich im wesentlichen in die folgenden Gruppen einteilen : ▻ Klassen für elementare GUI-Komponenten ▻ Klassen für GUI-Container ▻ Klassen zur Gestaltung der GUI-Komponenten (Layout-Manager, Farben und Fonts) ▻ Klassen und Interfaces zur Ereignisverarbeitung (Listener, Adapter und Events) ◇ Die JFC umfassen im wesentlichen zwei – sich teilweise ersetzende und teilweise ergänzende – Frameworks : ▻ Abstract Windows Toolkit (AWT) Package java.awt mit mehreren Unterpaketen ▻ Die Swing-Klassen Package javax.swing mit mehreren Unterpaketen ◇ Zusätzlich gehören zu den JFC : ▻ Java 2D API (Klassen zur Realisierung fortgeschrittener 2D-Graphik, Bild- und Textbearbeitung sowie Drucken) ▻ Accessibility API (Klassen zur Realisierung von Interfaces für Behinderte) ▻ Klassen zur Unterstützung international einsetzbarer GUI-Applikationen, die sich leicht an die jeweilige Landessprache und nationalen Konventionen anpassen. Hierzu gehört u.a. das Input Method Framework API. Abstract Windows Toolkit (AWT) ◇ Ursprüngliches GUI-Paket in Java. Seit dem JDK 1.0 enthalten Im JDK 1.1. wesentlich überarbeitet, insbesondere bezüglich der Ereignis-Bearbeitung ◇ Bildet auch die Grundlage für das spätere Swing-Paket. ◇ Die Implementierung der AWT-Klassen für die GUI-Komponenten verwendet die durch die Graphik-und WindowFunktionalität des jeweiligen Betriebssystems zur Verfügung gestellten Komponenten ("heavyweight" components). Das bedeutet, - dass nur solche Funktionalitäten implementiert werden konnten, die auf allen wichtigen Plattformen, die Java unterstützten, existierten ("kleinster gemeinsamer Nenner") - dass das Aussehen und die Bedienbarkeit ("Look and Feel", LaF) einer graphischen Benutzeroberfläche jeweils systemspezifisch ist. Die einzelnen GUI-Komponenten werden in der durch das jeweilige Betriebssystems vorgegebenen Art und Weise dargestellt. Die Swing -Klassen ◇ Framework zur Erstellung von GUI-Anwendungen mit erweiterter Funktionalität. Beim JDK 1.1 als Add-On verfügbar, seit dem JDK 1.2 fester Bestandteil der Java 2 Platform. ◇ Die Swing-Klassen bauen auf dem AWT auf, nutzen vieler seiner Grundfunktionalitäten, u.a. das Model und die Klassen für die Ereignisbearbeitung, sowie die Klassen zur Gestaltung der GUI-Komponenten. ◇ Die Implementierung der Klassen für die GUI-Komponenten – ausser den Top-Level-Containern – greift nicht mehr auf die Komponentendarstellung des Betriebssystems zurück. Sie ist vielmehr – unter Verwendung graphischer PrimitivOperationen ("lightweight components") vollkommen in Java realisiert. Das bedeutet, das das "Look and Feel" einer mit Swing-Komponenten realisierten GUI unabhängig vom jeweiligen Betriebssystem und damit für alle Systeme gleich ist. ◇ Andererseits kann der Programmierer und gegebenenfalls auch der Programmbenutzer das LaF einer GUI-Anwendung selbst festlegen (Pluggable Look and Feel), wobei zwischen einigen Standard-LaFs ausgewählt werden aber auch ein eigenes LaF gestaltet werden kann. Es ist sogar möglich das LaF dynamisch während des Programmlaufs zu verändern. Defaultmäßig ist für alle GUI-Komponenten ein Java Look and Feel (Name "Metal") eingestellt. ◇ AWT- und Swing-Klassen für GUI-Komponenten dürfen nicht miteinander gemischt verwendet werden. 3 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 522 – 00 – TH – 03 ------------------------------------------------------------------------------------ Java Foundation Classes – Überblick (2) Hierarchie der JFC-Klassen für GUI-Komponenten (unvollständig) Component Container Label JComponent Window AWT Button Frame … weitere AWTKomponenten Checkbox ScrollPane JWindow JFrame JDialog JPanel MenuItem Panel Menu Dialog Applet PopupMenu FileDialog JApplet JList JScrollPane JComboBox Box MenuComponent Swing CheckBoxMenuItem JPopupMenu JTable JMenuBar JToolBar ... ... AbstractButton MenuBar Box.Filler JLabel JTextComponent … weitere SwingKomponenten ... JButton ... JToggleButton JRadioButton JCheckBox JMenuItem JMenu JTextArea JCheckBoxMenuItem JRadioButtonMenuItem 4 JTextField JEditorPane JPasswordField JTextPane FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 523 – 00 – TH – 03 ------------------------------------------------------------------------------------ Java Foundation Classes – Überblick (3) Top-Level-Container AWT Swing Fenster mit Rahmen und Titelleiste Frame JFrame Fenster ohne Rahmen und Titelleiste Window JWindow Dialog-Box Dialog JDialog Applet Applet JApplet AWT Swing Beschriftung Label JLabel Zeichenfläche Canvas Schaltknopf Button Elementare Komponenten (Auswahl) JButton JToggleButton Umschaltknopf (Schaltknopf mit zwei Zuständen) Auswahlfeld Checkbox JCheckBox Gruppe alternativer Auswahlfelder (nur ein Feld kann ausgewählt werden) Checkbox in Verbindung mit CheckboxGroup JRadioButton in Verbindung mit ButtonGroup Auswahlliste List JList aufklappbare Auswahlliste Choice JComboBox einzeiliges Textfeld (editierbar) TextField JTextField mehrzeiliges Textfeld (editierbar) TextArea JTextArea Bildlaufleiste Scrollbar JScrollBar Bildlauffläche (horizontale u. vertikale Bildlaufleiste) ScrollPane JScrollPane JTable Tabelle Menüeintrag MenuItem JMenuItem AWT Swing Panel JPanel "Innere" Container (Auswahl) Gruppierungsfeld Box Gruppierungbox (festliegender Layout-Manager) Menüleiste MenuBar JMenuBar Menu Menu JMenu JToolBar Werkzeugleiste 5 FACHHOCHSCHULE MUENCHEN FAKULTÄT ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 524 – 00 – TH – 03 ----------------------------------------------------------------------------------- Java Foundation Classes – Überblick (4) Einige Swing-Komponenten (Java Look and Feel) (Klasse SwingCompDemo) 6 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 531 – 00 – TH – 04 ------------------------------------------------------------------------------------ Basisklassen der JFC-Hierachie (1) Die abstrakte Klasse Component ◇ Diese – im Package java.awt enthaltene – Klasse steht an der Spitze der Hierarchie der GUI-KomponentenKlassen. Sie definiert grundlegende Methoden, die in fast allen AWT- und Swing-Komponenten-Klassen zur Verfügung stehen. Lediglich die AWT-Klassen für Menü-Komponenten befinden sich in einer hiervon unabhängigen Klassen-Hierarchie. ◇ Einige wesentliche Methoden : (die Methoden zum Registrieren von Listener-Objekten für die Event-Behandlung sind nicht mit aufgeführt ) public void setBackground(Color c) Setzen der Hintergrund-Farbe auf c public void setForeground(Color c) Setzen der Vordergrund-Farbe auf c public void setFont(Font f) Setzen der in der Komponente verwendeten Schriftart public void setSize(int w, int h) Setzen der Breite (auf w) und Höhe (auf h) der Komponente (Angabe in Pixel) public void setLocation(int x, int y) Setzen der Position der Komponente (linke obere Ecke auf (x,y), Angabe in Pixel) public void setVisible(boolean b) Anzeigen / Verbergen der Komponente (b==true : Komponente sichtbar, sonst unsichtbar) public void setEnabled(boolean b) Aktivierung / Deaktivierung der Reaktion der Komponente auf Benutzereingaben (b==true : Komponente kann auf Benutzereingaben reagieren (Events erzeugen)) Defaultmässig sind alle Komponenten aktiviert public Color getBackground() Rückgabe der Hintergrund-Farbe public Color getForeground() Rückgabe der Vordergrund-Farbe public Font getFont() Rückgabe der verwendeten Schriftart public int getWidth() Rückgabe der aktuellen Breite der Komponente (in Pixel) public int getHeight() Rückgabe der aktuellen Höhe der Komponente (in Pixel) public boolean isVisable() Rückgabe des Sichtbarkeits-Status der Komponente true, wenn Komponente sichtbar, false, wenn nicht public boolean isEnabled() Rückgabe des Aktivierungs-Status der Komponente true, wenn Komponente aktiviert, false, wenn nicht public void paint(Graphics g) Zeichnen der Komponente unter Verwendung des GraphikContext-Objekts g. Wird vom System aufgerufen (Callback), z.B. wenn die Komponente erstmals sichtbar gemacht wird oder eine Zustands(z.B. Größen-) änderung erfolgt ist public void repaint() Aufforderung an das System, die Komponente neu zu zeichnen (mittels paint()) Kann vom Anwender-Code aufgerufen werden, wenn sich der Zustand des GUI-Objekts geändert hat. public void validate() Sicherstellung, dass die Komponente ein gültiges Layout hat (Diese Methode ist insbesondere für Container vorgesehen) public void requestFocusInWindow() Anforderung des Focus (wenn Top-Level-Cont. Focus besitzt ) 7 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 532 – 00 – TH – 03 ------------------------------------------------------------------------------------ Basisklassen der JFC-Hierachie (2) Die Klasse Container ◇ Diese – im Package java.awt definierte – Klasse ist die Basisklasse aller GUI-Container-Klassen. Auch die Klassen für einfache Swing-Komponenten sind – indirekt – von dieser Klasse abgeleitet. Sie ist selbst von der Klasse Component abgeleitet. ◇ Sie stellt – über die von Component geerbten und teilweise überschriebenen Methoden hinaus – insbesondere Methoden zur Verfügung, die das Verwalten (z.B. Einfügen, Entfernen) von GUI-Komponenten ermöglichen. ◇ Die in einem GUI-Container-Objekt enthaltenen GUI-Komponenten werden in einer Liste verwaltet, wobei die Reihenfolge der Listenelemente standardmässig durch die Reihenfolge ihres Einfügens festgelegt ist. Diese Reihenfolge bestimmt auch die Anordnungs-Reihenfolge der Komponenten entsprechend des jeweils festgelegten Layout-Managers. Es ist aber auch möglich, die Reihenfolge der Komponenten (Position bezüglich des gewählten Layouts) durch einen entsprechenden Parameter ("Index") der Einfüge-Methode explizit zu beeinflussen. ◇ Wenn in einen Container eine Komponente neu eingefügt oder entfernt wird, nachdem der Container bereits sichtbar ist, muß die von Component geerbte – aber überschriebene – Methode validate() aufgerufen werden. Dadurch wird der Layout-Manager des Containers veranlasst, das Layout entsprechend anzupassen. ◇ Die von Component geerbte Methode paint() ist so überschrieben, dass für alle im Container enthaltenen Komponenten deren paint()-Methode aufgerufen wird. ◇ Die wichtigsten Methoden zur Komponentenverwaltung sind : (die Methoden zum Registrieren von Listener-Objekten für die Event-Behandlung sind nicht mit aufgeführt ) public Component add(Component comp) Einfügen der Komponente comp am Ende des Containers public Component add(Component comp, int idx) Einfügen der Komponente comp an der Position idx public Component add(Component comp, Object constr) Einfügen der Komponente comp unter Berücksichtigung der durch constr festgelegten Einschränkungen public Component getComponent(int idx) Ermitteln der Komponente an der Position idx public Component[] getComponents() Ermitteln aller Komponenten des Containers public int getComponentCount() Ermitteln der Anzahl der Komponenten im Container public void remove(Component comp) Entfernen der Komponente comp aus dem Container public void remove(int idx) Entfernen der Komponente an der Position idx public void removeAll() Entfernen aller Komponenten aus dem Container public void setLayout(LayoutManager mgr) Setzen des Layout-Managers mgr für den Container public LayoutManager getLayout() Ermitteln des Layout-Managers des Containers ◇ Ein Container besitzt Randbereiche (insets), die nicht für die Aufnahme von Komponenten zur Verfügung stehen (z.B. die Titelleiste). Die Randbereiche werden in einem Objekt der Klasse Insets (Package java.awt) zusammengefasst. Die 4 Randbereiche (Angabe in Pixel) sind zugänglich über public-int-Datenkomponenten : top, left, bottom, right. Zur Ermittlung des Insets-Objekt eines Containers dient die Memberfunktion : public Insets getInsets() Ermittlung der Randbereiche des Containers 8 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 533 – 00 – TH – 03 ----------------------------------------------------------------------------------- Basisklassen der JFC-Hierachie (3) Die abstrakte Klasse JComponent ◇ Diese von Container abgeleitete und im Package javax.swing enthaltene Klasse ist Basisklasse aller Swing-Komponenten-Klassen mit Ausnahme der Top-Level-Container ◇ Die Klasse passt einige der von Container und Component geerbten Methoden an die Funktionalität und die speziellen Eigenschaften des Swing-Frameworks an. Zusätzlich definiert sie Methoden, die speziell für Swing-Komponenten von Bedeutung sind ◇ U.a. besitzen Swing-Komponenten die folgenden bei AWT-Komponenten nicht vorhandenen Besonderheiten : ▻ Swing-Komponenten können mit einer Umrandung versehen werden. Die Umrandung wird durch ein Objekt einer das Interface Border implementierenden Klasse festgelegt. Zur Erzeugung derartiger Border-Objekte stehen statische Methoden der Klasse BorderFactory zur Verfügung. ▻ Der Hintergrund von Swing-Komponenten kann durchsichtig (nicht-opak) oder undurchsichtig (opak) sein. AWT-Komponenten besitzen immer einen undurchsichtigen Hintergrund. In der Klasse JComponent ist als Default durchsichtig (nicht-opak) festgelegt. Allerdings hängt der tatsächliche Defaultwert dieser Eigenschaft bei den abgeleiteten Swing-Klassen i.a. von dem jeweils eingesetzten LaF ab. ▻ Swing-Komponenten können mit einem Tooltip ausgestattet werden. Hierbei handelt es sich um einen mit der Komponente verknüpften Hinweistext, der angezeigt wird, wenn der Mauszeiger für kurze Zeit über der Komponente verweilt. ▻ Für Swing-Komponenten können die dem Layout-Manager als Dimensionierungsvorschläge dienenden Werte für die maximale, die minimale und die bevorzugte Größe (Breite und Höhe) der Komponente sowie Vorschläge für die Ausrichtung einer Komponente (in x- und y-Richtung) explizit gesetzt werden. Die entsprechenden Ermittlungs-Methoden (get...(), z.B. getMaximumSize()) sind bereits in der Klasse Component definiert. ◇ Zu den wichtigsten der speziellen Swing-Komponenten-Methoden gehören : (die Methoden zum Registrieren von Listener-Objekten für die Event-Behandlung sind nicht mit aufgeführt ) public void setBorder(Border bord) Setzen des Border-Objekts bord als Umrandung für die Komponente public Border getBorder() Ermitteln der Umrandung der Komponente public void setOpaque(boolean opa) Setzen des Hintergrunds der Komponente auf undurchsichtig (opa==true) bzw durchsichtig (opa==false) public boolean isOpaque() Ermitteln, ob Hintergrund der Komponente undurchsichtig oder durchsichtig ist true, wenn undurchsichtig, false, wenn durchsichtig public void setToolTipText(String txt) Setzen des Tooltip-Textes für die Komponente public String getToolTipText() Ermitteln des Tooltip-Textes der Komponente public void setMaximumSize(Dimension d) Setzen der maximalen Größe der Komponente auf die durch d gegebene Breite und Höhe. Erzeugung eines Dimension-Objekts : new Dimension(breite, hoehe) public void setMinimumSize(Dimension d) Setzen der minimalen Größe der Komponente auf d public void setPreferredSize(Dimension d) Setzen der bevorzugten Größe der Komponente auf d public void setAlignmentX(float align) Setzen der Ausrichtung in hor. Richtung (0.0 ... 1.0) public void setAlignmentY(float align) Setzen der Ausrichtung in vert. Richtung (0.0 ... 1.0) 9 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 541 – 00 – TH – 04 ------------------------------------------------------------------------------------ Erstellung einer GUI-Anwendung in Java (1) Prinzipielle Vorgehensweise ◇ Jede GUI-Anwendung besitzt wenigstens ein Top-Level-Fenster, über das sie mit dem Anwender kommuniziert. Eine GUI-Anwendung kann auch mehrere Top-Level-Fenster besitzen. ◇ Für jedes Top-Level-Fenster wird eine eigene Klasse definiert. Der Aufbau dieser Fenster-Erzeugungs-Klasse kann unterschiedlich sein. Neben anderen bieten sich folgende Hauptvarianten an : ▻ Die Klasse instanziert ein Objekt der eingesetzten JFC-Top-Level-Container-Klasse (i.a. in ihrem Konstruktor) Dieses kann sie gegebenenfalls über eine Datenkomponente referieren.("has a container") ▻ Die Klasse ist von der eingesetzten JFC-Top-Level-Container-Klasse abgeleitet. Bei der Instanzierung der Klasse wird das Top-Level-Container-Objekt als Teilobjekt angelegt ("is a container"). ◇ Für normale Anwendungsprogramme werden als Top-Level-Container-Klassen i.a. die Klassen Frame (für AWTAnwendungen) bzw JFrame (für Swing-Anwendungen) eingesetzt. ◇ Die Konfigurierung eines Top-Level-Fensters und damit der graphischen Oberfläche erfolgt typischerweise im Konstruktor der Fenster-Erzeugungs-Klasse bzw in speziellen von diesem aufgerufenen Memberfunktionen : ▻ Gegebenenfalls explizite Erzeugung des Top-Level-Container-Objekts (s. oben) ▻ Festlegung des Erscheinungsbildes (ohne LaF, das wird i.a. von ausserhalb , z.B in der main()-Methode der Start-Klasse, festgelegt) : ▹ Setzen des Layout-Managers (wenn anders als Default) ▹ Setzen der Hintergrund- und Vordergrundfarbe (wenn anders als Default) ▹ Setzen der Schriftart (wenn überhaupt benötigt und anders als Default) ▹ Setzen der (Ausgangs-)Größe des Fensters Wenn die Fenster-Erzeugungs-Klasse von der Top-Level-Container-Klasse abgeleitet ist, wird das auch häufig von ausserhalb – nach der Instanziierung der Klasse – vorgenommen. ▹ Setzen des Titels des Fensters Dieser kann entweder dem Konstruktor der Klasse Frame bzw JFrame übergeben werden oder mittels der Memberfunktion setTitle() festgelegt werden. Auch dies erfolgt häufig von ausserhalb des Konstruktors der Fenster-Erzeugungs-Klasse, wenn diese von der Top-Level-Container-Klasse abgeleitet ist. ▻ Erzeugen, Konfigurieren und Einfügen der Komponenten-Objekte des Containers. Dies wird sinnvollerweise haeufig in eine eigene Methode ausgelagert. Zur Konfigurierung einer einzufügenden GUI-Komponente sind gegebenenfalls analoge Schritte wie bei der Konfigurierung des Top-Level-Fensters auszuführen. Je nach Komponente stehen u.U. noch zusätzliche Konfigurationsmöglichkeiten zur Verfügung (z. B. Setzen einer Umrandung) Anmerkung zum Einfügen der Komponenten: In die AWT-Top-Level-Container (Frame, Dialog, Window) werden Komponenten direkt eingefügt (mittels add()). In die Swing-Top-Level-Container ( JFrame, JDialog, JWindow) dagegen werden die Komponenten nicht direkt eingefügt. Sie verfügen über einen speziellen Einfüge-Container, die sogenannte content pane, in die alle aufzunehmenden Komponenten mittels add() einzufügen sind. ◇ Definition von Event-Listener-Klassen und Registrierung der Event-Listener-Objekte bei den einzelnen Komponenten. Dies wird ebenfalls sinnvollerweise haeufig in eine eigene Methode ausgelagert. Die Kommunikation mit dem Anwender und dem Rest des Anwendungsprogramms (Entity-Klassen) erfolgt nur über die durch die Event-Listener definierten Methoden (Callbacks !) ◇ Anzeigen des Top-Level-Containers (und aller in ihm enthaltenen Komponenten). Dies erfolgt durch Einschalten der Sichtbarkeit des Containers (Aufruf von setVisible(true)). Dies kann entweder ebenfalls im Konstruktor der Fenster-Erzeugungs-Klasse oder – wenn diese von der Top-LevelContainer-Klasse abgeleitet ist – von ausserhalb erfolgen. 10 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 542 – 00 – TH – 01 ----------------------------------------------------------------------------------- Erstellung einer GUI-Anwendung in Java (2) Beispiel einer sehr einfachen AWT-Anwendung Top-Level-Fenster (abgeleitet von Frame) mit einer Label-Komponente // AWTSimpleFrame.java import java.awt.*; public class AWTSimpleFrame extends Frame { public AWTSimpleFrame(String title) { super(title); Label lab1 = new Label("Hallo !", Label.CENTER); add(lab1); addWindowListener(new WindowClosingAdapter()); // Registrierung eines // Event-Listeners // zum Schliessen des Fensters setSize(300,160); } public static void main(String[] args) { AWTSimpleFrame fenster = new AWTSimpleFrame("Ein sehr einfaches AWT-Fenster"); fenster.setVisible(true); } } 11 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 543 – 00 – TH – 01 ----------------------------------------------------------------------------------- Erstellung einer GUI-Anwendung in Java (3) Beispiel einer sehr einfachen Swing-Anwendung Top-Level-Fenster (abgeleitet von JFrame) mit einer JLabel-Komponente // SwingSimpleFrame.java import javax.swing.*; import java.awt.*; public class SwingSimpleFrame extends JFrame { private Container c; public SwingSimpleFrame(String title) { super(title); JLabel lab1 = new JLabel("Hallo !", SwingConstants.CENTER); c = getContentPane(); c.add(lab1); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(320, 160); } public static void main(String[] args) { SwingSimpleFrame fenster = new SwingSimpleFrame( "Ein sehr einfaches Swing-Fenster"); fenster.setVisible(true); } } 12 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 551 – 00 – TH – 03 ------------------------------------------------------------------------------------ Die Klasse Color (Package java.awt) Allgemeines ◇ Objekte der Klasse Color dienen zur Beschreibung von Farben. Sie werden z.B. zum Setzen der Hintergrund- und Vordergrundfarbe von GUI-Komponenten benötigt ◇ Objekte dieser Klasse legen eine Farbe durch deren Rot- Grün- und Blau-Anteile (RGB-Werte) fest. Jeder Anteil wird durch einen int-Wert im Bereich 0 ... 255 repräsentiert. Er lässt sich auch durch einen float-Wert im Bereich 0.0 ... 1.0 angeben. ◇ Eine weitere Datenkomponente dient zur Beschreibung der Farb-Transparenz (Alpha-Wert). Auch dieser Wert wird entweder als int-Wert (Bereich 0 ... 255) oder als float-Wert (Bereich 0.0 ... 1.0) angegeben. Dabei bedeutet - der Wert 0 bzw 0.0 vollkommen durchsichtig (nicht opak), - der Wert 255 bzw 1.0 vollkommen undurchsichtig (opak) ◇ Objekte für 13 häufig verwendete Farben sind vordefiniert und stehen als Klassen-Konstante zur Verfügung : Von Color.BLACK über Color.GREEN bis Color.YELLOW Konstruktoren ◇ Beliebige Color-Objekte lassen sich unter Angabe der RGB-Werte und gegebenenfalls des Alpha-Werts mit Hilfe der folgenden Konstruktoren erzeugen: public Color(int r, int g, int b) Erzeugung eines Color-Objekts mit den angegebenen RGB-Werten und einem Alpha-Wert von 255 (vollkommen undurchsichtig) public Color(float r, float g, float b) Erzeugung eines Color-Objekts mit den angegebenen RGB-Werten und einem Alpha-Wert von 1.0 (vollkommen undurchsichtig) public Color(int r, int g, int b, int a) Erzeugung eines Color-Objekts mit den angegebenen RGB-Werten und dem angegebenen Alpha-Wert (a) public Color(float r, float g, float b, float a) Erzeugung eines Color-Objekts mit den angegebenen RGB-Werten und dem angegebenen Alpha-Wert (a) Memberfunktionen zur Ermittlung der Farb-Komponenten ◇ Neben zahlreichen anderen Memberfunktionen existieren die folgenden Methoden zum Ermitteln der einzelnen Farbkomponenten eines Color-Objekts. public int getRed() Ermittlung der Rot-Komponente im Bereich 0 ... 255 public int getGreen() Ermittlung der Grün-Komponente im Bereich 0 ... 255 public int getBlue() Ermittlung der Blau-Komponente im Bereich 0 ... 255 public int getAlpha() Ermittlung des Alpha-Werts im Bereich 0 ... 255 13 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 552 – 00 – TH – 02 ----------------------------------------------------------------------------------- Die Klasse Font (Package java.awt) Allgemeines ◇ Objekte der Klasse Font repräsentieren Schriftarten. ◇ Die Methode setFont() der Klasse Component dient zur Festlegung der in einer GUI-Komponente zu verwendenden Schriftart. Ihr ist ein Font-Objekt als Parameter zu übergeben. Wird für eine GUI-Komponente keine Schriftart explizit festgelegt (kein Aufruf der Methode setFont() oder ein Aufruf mit null als aktuellem Parameter), so erbt diese Komponente die in ihrem Container eingesetzte Schriftart. Defaultmässig wird ein systemabhängiger Standard-Font verwendet. ◇ Eine Schriftart wird durch drei Parameter festgelegt : ▻ Font-(Familien-)Name ▻ Schriftstil ▻ Schriftgröße ◇ Font-(Familien-)Name Als Font-(Familien-)Name wird i.a. ein Name für einen logischen Font angegeben, der von der Java-Laufzeitumgebung auf einen im System real vorhandenen Font (physikalischen Font) abgebildet wird. Von jedem Java-System werden die folgenden logischen Font-Familien-Namen unterstützt : ▻ "Serif" systemspezifische Proportionalzeichensatz-Familie TimesRoman Umsetzung unter Windows : True-Type-Font Times New Roman ▻ "SansSerif" systemspezifische Proportionalzeichensatz-Familie Helvetica Umsetzung unter Windows : True-Type-Font Arial ▻ "Monospaced" systemspezifische Nichtproportionalzeichensatz-Familie Courier Umsetzung unter Windows : True-Type-Font Courier New ◇ Schriftstil Spezifizierung durch einen int-Wert. Hierfür sind in der Klasse Font die folgenden Konstanten definiert : ▻ Font.PLAIN (0) normale Schrift ▻ Font.BOLD (1) fette Schrift ▻ Font.ITALIC (2) kursive Schrift Die Schriftstile Font.BOLD und Font.ITALIC können auch miteinander kombiniert werden (Addition oder bitweis-Oder) fette und kursive Schrift. ◇ Schriftgröße Angabe in Punkt (Pt) durch einen int-Wert. Übliche Punktgrößen für normale Textdarstellung : 10 oder 12 Pt. Konstruktor ◇ Font-Objekte können mit Hilfe des folgenden Konstruktors erzeugt werden : public Font(String name, int style, int size) Erzeugung eines neuen Font-Objekts mit dem Namen name, Stil style und Größe size Memberfunktionen zur Ermittlung der Font-Komponenten ◇ public String getName() Ermittlung des logischen Font-Familien-Namens public String getFamily() Ermittlung des physikalischen Font-Familien-Namens public int getStyle() Ermittlung des Schriftstils public int getSize() Ermittlung der Schriftgröße in Punkt 14 FACHHOCHSCHULE MUENCHEN FAKULTÄT ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 553 – 01 – TH – 04 ----------------------------------------------------------------------------------- Layout-Manager in Java (1) Allgemeines ◇ Üblicherweise wird die Anordnung (Größe und Position) der in einem Container enthaltenen Komponenten durch einen Layout-Manager vorgenommen. Prinzipiell können die einzelnen Komponenten zwar "Hinweise" zu ihrer Größe, Position und Ausrichtung enthalten, diese müssen aber nicht vom Layout-Manager berücksichtigt werden. Vielmehr hat dieser das "letzte Wort" bezüglich der Anordnung. ◇ Es ist auch möglich, auf einen Layout-Manager zu verzichten und mit absoluter Positionierung zu arbeiten. Dies erfordert dann eine genaue Festlegung der Größe und der Position jeder einzelnen Komponente und führt zu Anpassungsproblemen, wenn die Größe des Top-Level-Containers bzw die Ausführungs-Plattform verändert wird ◇ Ein Layout-Manager ist ein Objekt einer Klasse, die das Interface LayoutManager (Package java.awt) implementiert. Dieses Interface definiert Methoden, die für die Anordnung von GUI-Komponenten innerhalb eines Containers benötigt werden. ◇ Für erweiterte Layout-Fähigkeiten ist das von LayoutManager abgeleitete Interface LayoutManager2 definiert. Es enthält Methoden, die es einem Layout-Manager ermöglichen, durch contraints-Objekte festgelegte Anordnungs-Beschränkungen / -Vorgaben explizit zu berücksichtigen. contraints-Objekte (häufig String-Objekte) spezifizieren wie und wo Komponenten in das Layout einzufügen sind. ◇ Die Java-Bibliothek stellt eine Reihe von Layout-Manager-Klassen zur Verfügung. Die einzelnen Klassen unterscheiden sich insbesondere hinsichtlich der Unterteilung der Gesamtfläche eines Containers in verschiedene Bereiche und die Zuordnung dieser Bereiche zu den im Container enthaltenen Komponenten. Einige Layout-Manager passen dabei die Komponenten in ihrer Groesse an oder fügen Zwischenräume zwischen ihnen ein. Einige von ihnen implementieren nur das Interface LayoutManager, andere das Interface LayoutManager2. Layout-Manager-Klassen sind sowohl im Package java.awt als auch im Package javax.swing definiert Die am häufigsten verwendeten Layout-Manager-Klassen sind : ▻ BorderLayout (Package java.awt) ▻ FlowLayout (Package java.awt) ▻ GridLayout (Package java.awt) ▻ BoxLayout (Package javax.swing) ▻ GridBagLayout (Package java.awt) ◇ Defaultmässig ist in den meisten GUI-Container-Klassen das BorderLayout eingestellt. Ausnahmen : - Für die Klassen Panel und JPanel ist FlowLayout voreingestellt. - Für die Klasse Box (Package javax.swing) ist BoxLayout voreingestellt Ein anderer Layout-Manager kann mit der in der Klasse Container definierten Methode void setLayout(LayoutManager mgr) für jedes Container-Objekt individuell festgelegt werden Dabei lassen sich die im Package javax.swing definierten Layout-Manager-Klassen nur für Swing-Komponenten einsetzen, während die im Package java.awt enthaltenen Layout-Manager-Klassen sowohl für AWT- als auch für Swing-Komponenten Anwendung finden. 15 FACHHOCHSCHULE MUENCHEN FAKULTÄT ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 553 – 02 – TH – 02 ----------------------------------------------------------------------------------- Layout-Manager in Java (2) Die Klasse BorderLayout (Package java.awt) ◇ Dieser Layout-Manager teilt den Container in fünf Gebiete ein : "Norden", "Süden", "Westen", Osten" und "Zentrum". In jedes dieser Gebiete kann er genau eine Komponente einfügen. ◇ Die einzelnen Komponenten werden in ihrer Größe so angepasst, dass sie insgesamt den gesamten Container ausfüllen. Allerdings können sowohl horizontale als auch vertikale Abstände zwischen den Komponenten festgelegt werden. Die Komponenten im "Norden" und "Süden" bekommen ihre bevorzugte Höhe und werden in der Breite an die Containergröße angepasst Die Komponenten im "Westen" und "Osten" bekommen dagegen ihre bevorzugte Breite und werden in der Höhe an den Container angepasst. Die Komponente im "Zentrum" wird sowohl in der Höhe als auch in der Breite an den verbleibenden Bereich angepasst. ◇ Konstruktoren : public BorderLayout() Erzeugung eines BorderLayout-Objekts, das die Komponenten ohne Zwischenabstände anordnet public BorderLayout(int hgap, int vgap) Erzeugung eines BorderLayout-Objekts, das die Komponenten mit dem horizontalen Abstand hgap und dem vertikalen Abstand vgap anordnet (Abstaende in Pixel) ◇ Beim Einfügen einer Komponente in einen Container (mittels add()) ist üblicherweise das Gebiet, in dem sie platziert werden soll, anzugeben. Für diese Angabe sind die folgenden in der Klasse BorderLayout definierten (String-)Konstanten zu verwenden : - BorderLayout.NORTH - BorderLayout.SOUTH - BorderLayout.WEST - BorderLayout.EAST - BorderLayout.CENTER Beispiel : Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(new Button("Okay"), BorderLayout.SOUTH); Wird keine Gebietsangabe beim Einfügen angegeben, wird die Komponente im "Zentrum" platziert. ◇ Wird die Größe des Containers geändert (z.B. durch Ziehen mit der Maus), so bleibt die Höhe der Komponenten im "Norden" und "Süden" sowie die Breite der Komponenten im "Westen und "Osten" unverändert, während die jeweils andere Dimension dieser Komponenten sowie beide Dimensionen der Komponente im "Zentrum" an die Größe des Containers angepasst werden. ◇ Beispiel : Border-Layout eines JFrame-Containers mit fünf JButton-Komponenten 16 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 553 – 03 – TH – 01 ----------------------------------------------------------------------------------- Layout-Manager in Java (3) Demonstrationsbeispiel zu Layout-Manager (hier : BorderLayout) // LayoutDemo.java import java.awt.*; import javax.swing.*; public class LayoutDemo extends JFrame { Container c; JButton[] ba; String titel; public LayoutDemo() { c=getContentPane(); titel = "LayoutDemo : "; ba = new JButton[5]; for (int i=0; i<ba.length; i++) { ba[i] = new JButton("Button "+(i+1)); ba[i].setAlignmentX(Component.CENTER_ALIGNMENT); } ba[2].setText(ba[2].getText()+" with long title"); ba[2].setFont(new Font("Serif", Font.ITALIC|Font.BOLD, 12)); ba[2].setBackground(Color.ORANGE); useBorderLayout(); //useFlowLayout(); //useGridLayout(); //useBoxLayout(); setTitle(titel); setSize(300, 170); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void useBorderLayout() { c.add(ba[0], BorderLayout.NORTH); c.add(ba[1], BorderLayout.SOUTH); c.add(ba[2], BorderLayout.CENTER); c.add(ba[3], BorderLayout.WEST); c.add(ba[4], BorderLayout.EAST); titel+="BorderLayout"; } // BorderLayout ist Default bei JFrame private void useFlowLayout() { // } private void useGridLayout() { // } private void useBoxLayout() { // } public static void main(String[] args) { new LayoutDemo().setVisisble(true); } } 17 FACHHOCHSCHULE MUENCHEN FAKULTÄT ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 553 – 04 – TH – 02 ----------------------------------------------------------------------------------- Layout-Manager in Java (4) Die Klasse FlowLayout (Package java.awt) ◇ Dieser Layout-Manager fügt die Komponenten "fliessend" zeilenweise von links nach rechts in den Container ein. Dabei bedeutet "fliessend", dass die Komponenten solange in der Reihenfolge ihres Einfügens nebeneinander platziert werden, bis kein Platz mehr für die nächste Komponente vorhanden ist. Dann wird das Einfügen analog in der nächsten Reihe fortgesetzt. ◇ Die Komponenten werden in ihrer bevorzugten Größe (Breite und Höhe) dargestellt. Defaultmässig werden die in einer Reihe befindlichen Komponenten zentriert ausgerichtet. Zwischen den Komponenten wird sowohl horizontal als auch vertikal ein Abstand von 5 Pixel gesetzt. Der vertikale Abstand bezieht sich dabei auf die Komponenten mit der jeweils größten Höhe. Sowohl die Ausrichtung als auch der Abstand in beiden Richtungen können geändert werden. ◇ Konstruktoren : public FlowLayout() Erzeugung eines FlowLayout-Objekts, mit den Defaulteinstellungen (zentrierte Ausrichtung und vertikaler und horizontaler Abstand von 5 Pixel) public FlowLayout(int align) Erzeugung eines FlowLayout-Objekts, mit einer durch align festgelegten Ausrichtung und einem vertikalen und horizontalen Abstand von 5 Pixel public FlowLayout(int align, int hgap, int vgap) Erzeugung eines FlowLayout-Objekts, mit einer durch align festgelegten Ausrichtung und einem durch hgap (horizontal) und vgap (vertikal) gegebenen Abstand ◇ Zur Angabe der Komponenten-Ausrichtung in den einzelnen Zeilen (Konstruktor-Parameter align) stehen in der Klasse FlowLayout die folgenden Konstanten zur Verfügung : - FlowLayout.LEFT (Ausrichtung linksbündig) - FlowLayout.RIGHT (Ausrichtung rechtsbündig) - FlowLayout.CENTER (Ausrichtung zentriert, default) ◇ Wird die Größe des Containers geändert (z.B. durch Ziehen mit der Maus), so wird die Zuordnung der Komponenten zu den einzelnen Zeilen entsprechend angepasst. ◇ Beispiel : Flow-Layout eines JFrame-Containers mit fünf JButton-Komponenten (Ergänzung / Modifikation des obigen Demonstrationsprogramms) private void useFlowLayout() { c.setLayout(new FlowLayout()); for (int i=0; i<ba.length; i++) c.add(ba[i]); titel+="FlowLayout"; } 18 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 553 – 05 – TH – 02 ----------------------------------------------------------------------------------- Layout-Manager in Java (5) Die Klasse GridLayout (Package java.awt) ◇ Dieser Layout-Manager teilt die Containerfläche in gitter- bzw tabellenartig angeordnete Zellen auf. Die Zellen sind Rechtecke gleicher Größe. Diese Größe ergibt sich aus der Containergröße und der Anzahl von Zeilen und Spalten. In jedem dieser Rechtecke wird genau eine Komponente platziert. Standardmässig (horizontale Ausrichtung,von links nach rechts) werden die Komponenten in der Reihenfolge ihres Einfügens zeilenweise von links nach rechts angeordnet. ◇ Die Größe der Komponenten wird an die Zellen-Größe angepasst, so dass der in der Zelle verfügbare Platz voll ausgenutzt wird. Alle Komponenten haben somit die gleiche Größe. Defaultmässig besteht zwischen den Zellen kein Abstand. Es kann aber sowohl ein vertikaler als auch ein horizontaler Abstand festgelegt werden. ◇ Die Anzahl der Zeilen und Spalten können angegeben werden (im Konstruktor oder mittels Memberfunktionen). Wenigstens einer der beiden Werte muss von 0 verschieden sein. Der Wert 0 bedeutet "beliebig viele". Wenn beide Werte von 0 verschieden sind, wird der Wert für die Anzahl der Spalten ignoriert. In diesem Fall wird die tatsächliche Anzahl der Spalten aus der festgelegten Anzahl der Zeilen und der Komponenten-Anzahl ermittelt. Die explizite Angabe einer Spalten-Anzahl beeinflusst das Layout nur dann, wenn für die Anzahl der Zeilen 0 angegeben wird. Wird weder die Anzahl der Zeilen noch die Anzahl der Spalten explizit festgelegt, wird die Zeilenzahl defaultmässig auf 1 gesetzt. Die Anzahl der Spalten ergibt sich dann aus der Anzahl der Komponenten. ◇ Konstruktoren : public GridLayout() Erzeugung eines GridLayout-Objekts, mit einer Zeile, beliebig vielen Spalten, kein Abstand zwischen den Zellen public GridLayout(int rows, int cols) Erzeugung eines GridLayout-Objekts, mit rows Zeilen und cols Spalten (tatsächliche Auswirkung siehe oben), kein Abstand zwischen den Zellen public GridLayout(int rows, int cols, int hgap, int vgap) Erzeugung eines GridLayout-Objekts, mit rows Zeilen und cols Spalten (siehe oben), sowie einem durch hgap (horizontal) und vgap (vertikal) gegebenen Zellen-Abstand ◇ Wird die Größe des Containers geändert (z.B. durch Ziehen mit der Maus), so wird – bei gleichbleibender Zeilenund Spaltenzahl – die Größe der Zellen und damit die Größe der Komponenten an die jeweilige Gesamtgröße des Containers angepasst. ◇ Beispiel : Grid-Layout eines JFrame-Containers mit fünf JButton-Komponenten (3 Zeilen, 2 Spalten) (Ergänzung / Modifikation des obigen Demonstrationsprogramms) private void useGridLayout() { c.setLayout(new GridLayout(3,2)); for (int i=0; i<ba.length; i++) c.add(ba[i]); titel+="GridLayout(3,2)"; } 19 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 553 – 06 – TH – 02 ------------------------------------------------------------------------------------ Layout-Manager in Java (6) Die Klasse BoxLayout (Package javax.swing) ◇ Dieser Layout-Manager ordnet die Komponenten – gemäß ihrer Einfüge-Reihenfolge - entweder horizontal in einer Zeile oder vertikal in einer Spalte an. Die Anordnungs-Achse ist jeweils festzulegen (kein Default). ◇ Er berücksichtigt hierbei sowohl die für die einzelnen Komponenten eingestellten Größen-Parameter (minimale, maximale und bevorzugte Größe) als auch deren festgelegte vertikale und horizontale Ausrichtung. Grundsätzlich versucht er die Komponenten in ihrer bevorzugten Größe darzustellen. Sind bei vertikaler Anordnung die bevorzugten Breiten der Komponenten unterschiedlich, wird versucht, unter Berücksichtigung der jeweiligen maximalen Breiten, die Breite aller Komponenten an die größte bevorzugte Breite anzupassen. Ist das nicht möglich, z.B. weil die maximalen Größen es nicht zulassen, werden die Komponenten in ihrer bevorzugten Breite dargestellt. Die horizontale Ausrichtung der Komponenten (sowohl untereinander als auch innerhalb des Containers) richtet sich nach deren – defaultmässig vorgegebener bzw explizit gesetzter – horizontaler (X-) Ausrichtung. Analoges gilt für die horizontale Anordung. Werden die Komponenten mit unterschiedlicher Höhe dargestellt, richtet sich ihre vertikale Ausrichtung nach der für die einzelnen Komponenten eingestellten vertikalen (Y-) Ausrichtung (Default ist.i.a. zentriert) ◇ Zwischen den Komponenten wird kein Abstand eingefügt. Zum Erzeugen von Abständen können spezielle unsichtbare Füll-Komponenten eingesetzt werden. ◇ Konstruktor : public BoxLayout(Container con, int axis) Erzeugung eines BoxLayout-Objekts für das Container-Objekt con mit der durch axis festgelegten Anordnungs-Achse ◇ Zur Festlegung der Anordnungs-Achse (Konstruktor-Parameter axis) stehen in der Klasse BoxLayout die folgenden Konstanten zur Verfügung : - BoxLayout.X_AXIS (horizontale Anordnung, in einer Zeile) - BoxLayout.Y_AXIS (vertikale Anordnung, in einer Spalte) - BoxLayout.LINE_AXIS (Bedeutung sprachabhängig, bei europäischen Sprachen : horizontale Anordnung) - BoxLayout.PAGE_AXIS (Bedeutung sprachabhängig, bei europäischen Sprachen : vertikale Anordnung) ◇ Bei einer Größenänderung des Containers (z.B. durch Ziehen mit der Maus), behalten die Komponenten ihre ursprüngliche Größe, Anordnung und Ausrichtung (zueinander und im Container). ◇ Beispiel : Box-Layout eines JFrame-Containers mit fünf JButton-Komponenten (vertikale Anordnung) (Ergänzung / Modifikation des obigen Demonstrationsprogramms) private void useBoxLayout() { c.setLayout(new BoxLayout(c, BoxLayout.Y_AXIS)); for (int i=0; i<ba.length; i++) c.add(ba[i]); titel+="BoxLayout"; } 20 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 554 – 01 – TH – 04 ----------------------------------------------------------------------------------- Umrandungen für Swing-Komponenten in Java (1) Umrandungen und das Interface Border (Package javax.swing.border) ◇ Swing-GUI-Komponenten (genauer : Objekte aller von JComponent abgeleiteten Klassen) können mit einer Umrandung versehen werden. Derartige Umrandungen können u.a. dazu dienen : - GUI-Komponenten mit einer Dekoration zu versehen - um rahmenlose GUI-Komponenten einen Rahmen zu zeichnen - für GUI-Komponenten einen Namen anzugeben - GUI-Komponenten mit Abstandsflächen zu versehen. ◇ Umrandungen werden durch Border-Objekte beschrieben. Zum Setzen einer Umrandung muß die in JComponent definierte Memberfunktion setBorder() mit einem Border-Objekt als Parameter für das entsprechende GUI-Komponenten-Objekt aufgerufen werden. ◇ Border-Objekte sind Objekte von Klassen, die das Interface Border implementieren. Klassen für Border-Objekte ◇ In der Java-Standard-Bibliothek – u.a. im Package javax.swing.border – sind zahlreiche Klassen, die das Interface Border implementieren, definiert. Einige der im Package javax.swing.border enthaltenen Klassen sind : ▻ AbstractBorder abstrakte Basisklasse für konkrete Border-Klassen ▻ EmptyBorder Klasse für "leere" Umrandungen Abstandsflächen ▻ LineBorder Klasse für Umrahmungen mit "normalen" Linien ▻ EtchedBorder Klasse für Umrahmungen mit eingeprägten bzw herausgearbeiteten Linien ▻ TitledBorder Klasse für mit einem Titel versehene Umrandungen ▻ CompoundBorder Klasse zur Zusammenfassung von zwei Umrandungen zu einer einzigen ◇ Es ist auch möglich eigene Border-Klassen zu definieren. Diese sind zweckmässigerweise von der Klasse AbstractBorder abzuleiten. Anwendungung von Umrandungen ◇ Prinzipiell können Umrandungen um jedes Objekt einer von JComponent abgeleiteten Klasse gesetzt werden. ◇ Jedoch arbeitet die LaF-Implementierung vieler Standard-Swing-Komponenten nicht sehr gut mit explizit gesetzten Umrandungen. Es wird daher empfohlen, Umrandungen grundsätzlich nur für JPanel- und JLabel-Komponenten zu verwenden. Bei Komponenten dieser Klassen treten keine Probleme auf. ◇ Sollen andere Swing-Komponenten mit einer Umrandung versehen werden, so sollten diese zweckmässigerweise in jeweils eine JPanel-Komponente eingebettet werden und um diese dann die Umrandung gesetzt werden. Erzeugung von Border-Objekten ◇ Ein Border-Objekt kann von mehreren GUI-Komponenten-Objekten gemeinsam genutzt werden. Daher ist es üblich und zweckmässig , Border-Objekte nicht mittels eines new-Ausdrucks zu erzeugen, sondern hierfür statische Erzeugungs-Methoden der Klasse BorderFactory (Package javax.swing) einzusetzen (Verwendung von Border-Objekten als Singletons). 21 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 554 – 02 – TH – 05 ------------------------------------------------------------------------------------ Umrandungen für Swing-Komponenten in Java (2) Die Klasse BorderFactory (Package javax.swing) ◇ Die Klasse BorderFactory stellt u.a. die folgenden statischen Erzeugungs-Methoden für Border-Objekte bereit (Auswahl) : public static Border createEmptyBorder(int top, int left, int bottom, int right) Erzeugung eines EmptyBorder-Objekts mit den durch die Parameter festgelegten Größen (in Pixel) public static Border createLineBorder(Color col, int thick) Erzeugung eines LineBorder-Objekts, das die Farbe col und die Linien-Stärke thick (in Pixel) verwendet public static Border createLineBorder(Color col) Erzeugung eines LineBorder-Objekts, das die Farbe col und einen Defaultwert für die Linien-Stärke verwendet public static Border createEtchedBorder() Erzeugung eines EtchedBorder-Objekts für eingeprägte (etched-in) Umrandungen, das die aktuelle Hintergrundfarbe des zu umrandenden Objekts für Hervorhebungen und Abschattungen verwendet public static Border createEtchedBorder(int type) Erzeugung eines EtchedBorder-Objekts für den durch type festgelegten Umrandungstyp, das die aktuelle Hintergrundfarbe des zu umrandenden Objekts für Hervorhebungen und Abschattungen verwendet Zulässige Werte für type : EtchedBorder.RAISED herausgearbeitete Umrandung (etched-out) EtchedBorder.LOWERED eingeprägte Umrandung (etched-in) public static Border createEtchedBorder(int type, Color highlight, Color shadow) Erzeugung eines EtchedBorder-Objekts für den durch type festgelegten Umrandungstyp, das die Farbe highlight für Hervorhebungen und die Farbe shadow für Abschattungen verwendet public static TitledBorder createTitledBorder(String str) Erzeugung eines TitledBorder-Objekts, das die Beschriftung str und Defaultwerte für die übrigen Kenngrößen verwendet. Diese Defaultwerte sind : eingeprägte (?) Umrandung, Text auf oberem Rand, linksbündig, Font und Textfarbe durch die Defaultwerte des aktuellen LaF bestimmt public static TitledBorder createTitledBorder(Border bord, String str) Erzeugung eines TitledBorder-Objekts aus dem existierenden Border-Objekt bord, das die Beschriftung str sowie Defaultwerte für die Textposition (oberer Rand), die Textausrichtung (linksbündig) und Font und Textfarbe (Defaultwerte des aktuellen LaF) verwendet. public static TitledBorder createTitledBorder(Border bord, String str, int just, int pos) Erzeugung eines TitledBorder-Objekts aus dem existierenden Border-Objekt bord, das die Beschriftung str sowie die Textposition pos, die Textausrichtung just und Defaultwerte für Font und Textfarbe (Defaultwerte des aktuellen LaF) verwendet. Zulässige Werte für just und pos siehe Java-API-Doc public static TitledBorder createTitledBorder(Border bord, String str, int just, int pos, Font fnt, Color col) Erzeugung eines TitledBorder-Objekts aus dem existierenden Border-Objekt bord, das die Beschriftung str sowie die Textposition pos, die Textausrichtung just, den Font fnt und die Farbe col verwendet. Zulässige Werte für just und pos siehe Java-API-Doc public static CompoundBorder createCompoundBorder(Border out, Border in) Erzeugung eines CompoundBorder-Objekts aus den existierenden Border-Objekten out (äussere Umrandung) und in (innere Umrandung) 22 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 554 – 03 – TH – 02 ------------------------------------------------------------------------------------ Umrandungen für Swing-Komponenten in Java (3) Demonstrationsprogramm zu Umrandungen // MultiBorderDemo.java import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class MultiBorderDemo extends JFrame { Container c; JButton[] ba; String titel; public MultiBorderDemo(int sel) { c=getContentPane(); c.setLayout(new FlowLayout()); titel = "MultiBorderDemo"; JPanel[] pa = new JPanel[5]; for (int i=0; i<pa.length; i++) { pa[i] = new JPanel(); JButton but = new JButton("Button "+(i+1)); if(i==2) { but.setText(but.getText()+" with long title"); but.setFont(new Font("Serif", Font.ITALIC|Font.BOLD, 12)); but.setBackground(Color.ORANGE); } pa[i].add(but); switch (sel) { case 1 : pa[i].setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); break; case 2 : pa[i].setBorder(BorderFactory.createEtchedBorder()); break; case 3 : pa[i].setBorder(BorderFactory.createTitledBorder("button")); break; case 4 : Border inbord = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); Border outbord = BorderFactory.createLineBorder(Color.GREEN, 5); pa[i].setBorder(BorderFactory.createCompoundBorder(outbord, inbord)); break; default : break; } c.add(pa[i]); } setTitle(titel); setSize(300, 240); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { if (args.length==1) new MultiBorderDemo(Integer.parseInt(args[0])).setVisible(true); else new MultiBorderDemo(0).setVisible(true); } } 23 FACHHOCHSCHULE MUENCHEN FAKULTÄT ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 554 – 04 – TH – 01 ----------------------------------------------------------------------------------- Umrandungen für Swing-Komponenten in Java (4) Beispiele für Umrandungen (vom Demonstrationsprogramm MultiBorderDemo erzeugte Fenster) Umrandung mit EmptyBorder keine Umrandung Umrandung mit EtchedBorder Umrandung mit TitledBorder Umrandung mit CompoundBorder aus Etchedborder und LineBorder 24 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 561 – 00 – TH – 06 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (1) Die Klasse JFrame (Package javax.swing) ◇ Wichtigste Swing-Top-Level-Container-Klasse ◇ Im Unterschied zur AWT-Top-Level-Container-Klasse Frame, von der JFrame abgeleitet ist, werden in einen JFrame-Container die Komponenten nicht direkt eingefügt. Vielmehr besitzt jeder JFrame-Container, wie jeder andere Swing-Top-Level-Container, einen speziellen Einfüge-Container, die sogenannte content pane. Die content pane umfasst den gesamten "normalen" Einfüge-Bereich des Top-Level-Containers. In den Top-LevelContainer aufzunehmende Komponenten werden in seine content pane eingefügt. Auch das Setzen eines Layout-Managers erfolgt in der content pane und nicht direkt im JFrame-Container. Anmerkung : Ab dem JDK 5.0 sind die Methoden add(), remove() und setLayoutManager() für die Klasse JFrame so überschrieben, dass sie implizit die content pane verwenden. Sie können damit direkt für JFrame-Container aufgerufen werden. ◇ Zusätzlich kann ein JFrame-Container über eine Menü-Leiste verfügen. Falls vorhanden, befindet sich diese ausserhalb der content pane. Sie wird direkt in den Top-Level-Container eingefügt. Weiterhin kann ein JFrame-Container eine Werkzeugleiste besitzen. Diese wird analog zu anderen Komponenten in die content pane eingefügt. ◇ Als weiterer Unterschied zur AWT-Klasse Frame ist in der Klasse JFrame bereits – ohne explizite Registrierung eines Window-Listeners – eine Reaktions-Funktionalität auf das Schliessen des Fensters implementiert. Die genaue Reaktion kann mittels der Memberfunktion public void setDefaultCloseOperation(int op) festgelegt werden. Als gültige Werte für den Parameter op können die folgenden Konstanten verwendet werden : - WindowConstants.DO_NOTHING_ON_CLOSE keine Reaktion - WindowConstants.HIDE_ON_CLOSE das Fenster verstecken (unsichtbar machen), Default - WindowConstants.DISPOSE_ON_CLOSE das Fenster verstecken und zerstören - JFrame.EXIT_ON_CLOSE das Programm beenden (mittels System.exit(0)) ◇ Konstruktoren (Auswahl) public JFrame() Erzeugung eines JFrame-Objekts ohne Titel, das durch das Objekt repräsentierte Fenster ist unsichtbar public JFrame(String title) Erzeugung eines JFrame-Objekts mit dem Titel title, das durch das Objekt repräsentierte Fenster ist unsichtbar ◇ Memberfunktionen (Auswahl) *) geerbt von Frame **) geerbt von Window public Container getContentPane() Ermitteln der content pane public void setDefaultCloseOperation(int op) Setzen der Reaktion auf das Schliessen des Fensters public void setJMenuBar(JMenuBar menu) Einfügen der Menuleiste menu public void setTitle(String title) *) Setzen des Titels auf title public void dispose() Zerstören des Fensters public void pack() **) **) Anpassen der Fenstergröße so, dass alle darin enthaltenen Komponenten gerade Platz haben (Berechnung des Platzbedarfs an Hand der bevorzugten Größe der Komponenten) ◇ Beispiel zum Einfügen von Komponenten JFrame frame = new JFrame(); Container c = frame.getContentPane(); c.setLayout(new FlowLayout()); // ab JDK 5.0 auch : frame.setLayout(...) c.add(new JLabel("Hallo")); // ab JDK 5.0 auch : frame.add(...) 25 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 562 – 00 – TH – 07 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (2) Die Klasse JDialog (Package javax.swing) ◇ Diese Top-Level-Container-Klasse wird typischerweise dazu benutzt, temporär auf dem Bildschirm erscheinende Fenster zu erzeugen, die für einen kurzen Dialog mit dem Benutzer vorgesehen sind ( Dialog-Fenster, Dialog-Box) ◇ Ein Dialog-Fenster ist üblicherweise an einen JFrame-Container oder ein anderes Dialog-Fenster gebunden ("Besitzer" des Dialog-Fensters). Es wird zusammen mit seinem "Besitzer" ikonifiziert/de-ikonifiziert oder geschlossen. ◇ Dialog-Fenster können modal gestaltet werden. Das bedeutet, dass während der Sichtbarkeit eines Dialog-Fensters alle anderen Fenster des Programms (also auch sein "besitzendes" Fenster) für Benutzereingaben gesperrt ist. Die anderen Fenster können erst wieder verwendet werden, wenn der Dialog abgewickelt, d.h. das Dialog-Fenster wieder geschlossen ist. ◇ Wie ein JFrame-Objekt besitzt auch ein JDialog-Objekt eine content pane. In diese – und nicht direkt in das JDialog-Objekt – müssen aufzunehmende Komponenten eingefügt werden. Auch ein eventueller Layout-Manager ist ebenfalls in der content pane zu setzen. Anmerkung : ab dem JDK 5.0 erfolgt dies implizit durch die für JDialog entsprechend überladenen Methoden add() und setLayoutManager() (sowie das Entfernen durch remove()) ◇ Ein JDialog-Objekt kann auch eine Menue-Leiste besitzen. Diese wird – wie bei JFrame-Objekten – direkt in den Top-Level-Container und nicht in die content pane eingefügt. ◇ Konstruktoren (Auswahl) public JDialog() Erzeugung eines nicht-modalen JDialog-Objekts ohne Titel und ohne explizit spezifizierten "Besitzer". Ein verborgenes Frame-Objekt wird implizit als "Besitzer" gesetzt public JDialog(Frame owner) Erzeugung eines nicht-modalen JDialog-Objekts ohne Titel und dem Frame-(JFrame-)Objekt owner als "Besitzer" public JDialog(Frame owner, String tit) Erzeugung eines nicht-modalen JDialog-Objekts mit dem Titel tit und dem Frame-(JFrame-)Objekt owner als "Besitzer" public JDialog(Frame owner, boolean mod) Erzeugung eines JDialog-Objekts ohne Titel und dem Frame-(JFrame-) Objekt owner als "Besitzer". Der Parameter mod bestimmt die Modalität : modal, wenn true, nicht-modal, wenn false public JDialog(Frame owner, String tit, boolean mod) Erzeugung JDialog-Objekts mit dem Titel tit und dem Frame(JFrame-) Objekt owner als "Besitzer". Der Parameter mod bestimmt die Modalität : modal, wenn true, nicht-modal, wenn false Es existieren weitere Konstruktoren, mit denen statt eines Frame-(JFrame)-Objekts ein Dialog-(JDialog-) Objekt als "Besitzer" festgelegt wird. ◇ Memberfunktionen (Auswahl) Die als Auswahl bei der Klasse JFrame angegebenen Memberfunktionen existieren auch für die Klasse JDialog : public public public public public public Container getContentPane() void setDefaultCloseOperation(int op) void setJMenuBar(JMenuBar menu) setTitle(String title) *) geerbt von void dispose() **) geerbt von void pack() **) geerbt von Der Wert EXIT_ON_CLOSE für op ist nicht zulässig Dialog Window Window Einige weitere Memberfunktionen (geerbt von Dialog) : public void setModal(boolean mod) Setzen der Modalität auf modal, wenn mod == true public boolean isModal() Ermittlung der Modalität, Rückgabewert : true, wenn modal, false andernfalls 26 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 563 – 01 – TH – 03 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (3-1) Die Klasse JOptionPane (Package javax.swing) ◇ Diese Klasse ermöglicht auf relativ einfache Art und Weise die Erzeugung einfacher Standard-Dialog-Fenster. ◇ Die erzeugbaren Standard-Dialog-Fenster informieren den Programmbenutzer über irgendetwas und/oder fordern ihn zu einer Eingabe auf. ◇ Die erzeugbaren Standard-Dialog-Fenster sind modal und können in der Größe nicht verändert werden. ◇ Als einfachste Möglichkeit zur Dialog-Fenster-Erzeugung stehen eine Reihe statischer Methoden zur Verfügung. Diese Methoden ▻ erzeugen ein weitgehend vorkonfiguriertes Dialog-Fenster, stellen es dar, ▻ warten auf eine zum Schliessen des Fensters führende Benutzeraktion (Schaltknopf-Betätigung, String-Eingabe) ▻ und liefern gegebenenfalls das Ergebnis der Benutzeraktion als Funktionswert zurück. ◇ Einige statische Klassen-Methoden als Beispiele : Der bei allen Methoden vorhandene Parameter parent legt die Komponente fest, für die der Dialog jeweils ausgeführt wird (null ist zulässig) public static void showMessageDialog(Component parent, Object msg, String title, int typeMsg) Erzeugen eines Meldungs-Dialog-Fensters mit dem Titel title, das die Meldung msg (meist ein String) ausgibt und ein durch den Meldungs-Typ typeMsg festgelegtes Default-Icon darstellt. Zulässige Werte für den Meldungs-Typ typeMsg : JOptionPane.ERROR_MESSAGE JOptionPane.INFORMATION_MESSAGE JOptionPane.WARNING_MESSAGE JOptionPane.QUESTION_MESSAGE JOptionPane.PLAIN_MESSAGE (kein Icon) public static void showMessageDialog(Component parent, Object msg) Erzeugen eines Meldungs-Dialog-Fensters mit dem Titel "Message", das die Meldung msg (meist ein String) ausgibt und das Default-Icon für den Typ INFORMATION_MESSAGE darstellt public static int showConfirmDialog(Component parent, Object msg, String title, int typeOpt) Erzeugen eines Bestätigungs-Dialog-Fensters mit dem Titel title, das die Meldung msg (meist ein String) ausgibt und das Icon für QUESTION_MESSAGE sowie die durch den Options-Typ typeOpt festgelegten Options-Schaltknöpfe darstellt und auf die Auswahl eines Schaltknopfes wartet. Zulässige Werte für den Options-Typ typeOpt : JOptionsPane.YES_NO_CANCEL_OPTION JOptionsPane.YES_NO_OPTION JOptionsPane.OK_CANCEL_OPTION JOptionsPane.DEFAULT_OPTION (nur OK) Der vom Benutzer ausgewählte Options-Schaltknopf bestimmt den zurückgegebenen Funktioswert (z.B. YES_OPTION) public static int showConfirmDialog(Component parent, Object msg) Erzeugen eines Bestätigungs-Dialog-Fensters mit einem Default-Titel, das die Meldung msg (meist ein String) ausgibt und das Icon für QUESTION_MESSAGE sowie die Schaltknöpfe für den Options-Typ YES_NO_CANCEL_OPTION darstellt und auf die Auswahl eines Schaltknopfes wartet. Der vom Benutzer ausgewählte Options-Schaltknopf bestimmt den zurückgegebenen Funktioswert (z.B. YES_OPTION) public static String showInputDialog(Component parent, Object msg) Erzeugen eines Frage-Dialog-Fensters mit dem Titel "Input" (bzw "Eingabe"), das die Meldung msg (meist ein String) ausgibt, das Icon für QUESTION_MESSAGE darstellt und auf die Eingabe eines Strings wartet. Der vom Benutzer eingegebene String wird als Funktionswert zurückgegeben. 27 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 563 – 02 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (3-2) Beispiele für von JOptionPane erzeugte Standard-Dialog-Fenster // Meldungs-Dialog : Fehler-Meldung JOptionPane.showMessageDialog(this, "Lesefehler !", "Eine Fehler-Meldung", JOptionPane.ERROR_MESSAGE); // Meldungs-Dialog : Information JOptionPane.showMessageDialog(this, "Die Festplatte ist zerstoert!", "Eine Information", JOptionPane.INFORMATION_MESSAGE); // Meldungs-Dialog : Warnung JOptionPane.showMessageDialog(this, "Hoeren Sie auf !", "Eine Warnung", JOptionPane.WARNING_MESSAGE); // Meldungs-Dialog : Frage JOptionPane.showMessageDialog(this, "Wollen Sie das wirklich ?", "Eine Frage", JOptionPane.QUESTION_MESSAGE); // Bestätigungs-Dialog // Default-Titel und // Default-Options-Typ int opt; opt = JOptionPane.showConfirmDialog(this, "Was wollen Sie ?"); // Frage-Dialog // Default-Titel String name; name = JOptionPane.showInputDialog(this, "Ihr Name ? "); 28 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 564 – 00 – TH – 02 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (4) Die Klasse JLabel (Package javax.swing) ◇ Diese Klasse dient zur Erzeugung von Beschriftungs-Objekten. ◇ Im Unterschied zu den AWT-Label-Objekten können JLabel-Objekte nicht nur Text, sondern auch Bilder darstellen (auch beides kombiniert) ◇ Die Ausrichtung sowohl des Textes als auch des Bildes innerhalb des Darstellungsbereichs eines JLabel-Objekts kann festgelegt werden. Default-Ausrichtung für Text : horizontal linksbündig, vertikal zentriert Default-Ausrichtung für Bilder : horizontal und vertikal zentriert Explizite Festlegung der Ausrichtung : SwingConstants.CENTER SwingConstants.RIGHT SwingConstants.LEFT SwingConstants.TOP SwingConstants.BOTTOM (zentriert) (rechtsbündig) (linksbündig) (obenbündig) (untenbündig) ◇ JLabel-Objekte können nicht auf vom Benutzer ausgelöste Ereignisse (Eingaben) reagieren. ◇ Konstruktoren (Auswahl) public JLabel(String text) Erzeugung eines JLabel-Objekts mit dem Text text, der Text ist horizontal linksbündig ausgerichtet public JLabel(String text, int halign) Erzeugung eines JLabel-Objekts mit dem Text text, der Text ist horizontal gemäß halign ausgerichtet public JLabel(Icon image) Erzeugung eines JLabel-Objekts mit dem Bild image, das Bild ist horizontal zentriert ausgerichtet public JLabel(Icon image, int halign) Erzeugung eines JLabel-Objekts mit dem Bild image, das Bild ist horizontal gemäß halign ausgerichtet ◇ Memberfunktionen (Auswahl) public String getText() Rückgabe des dargestellten Textes public Icon getIcon() Rückgabe des dargestellten Bildes public void setText(String text) Setzen des darzustellenden Textes auf text public void setIcon(Icon image) Setzen des darzustellenden Bildes auf image public void setHorizontalAlignment(int halign) Setzen der horizontalen Ausrichtung gemäß halign (für Text und Bild) public void setVerticalAlignment(int valign) Setzen der vertikalen Ausrichtung gemäß valign (für Text und Bild) ◇ Anmerkungen zu darzustellenden Bildern ▻ Bilder werden als Instanzen des Interfaces Icon (Package javax.swing) referiert. Im Regelfall handelt es sich bei Ihnen um Objekte der dieses Interface implementierenden Klasse ImageIcon ▻ Objekte der Klasse ImageIcon (Package javax.swing) können aus einer Bild-Datei erzeugt werden mit dem Konstruktor : public ImageIcon(String dateipfad) Der Parameter dateipfad spezifiziert die Bild-Datei. 29 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 565 – 00 – TH – 03 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (5) Die Klasse JButton (Package javax.swing) ◇ Diese Klasse dient zur Erzeugung von einfachen Schaltknöpfen (Schaltflächen). ◇ Auch JButton-Objekte können – im Unterschied zu den AWT-Button-Objekten – sowohl Text als auch ein Bild (auch beides kombiniert) darstellen. ◇ Auch bei JButton-Objekten kann die Ausrichtung sowohl des Textes als auch des Bildes innerhalb ihres Darstellungsbereichs festgelegt werden. Default-Ausrichtung für Text und Bilder : horizontal rechtsbündig, vertikal zentriert Explizite Festlegung der Ausrichtung : SwingConstants.CENTER SwingConstants.RIGHT SwingConstants.LEFT SwingConstants.TOP SwingConstants.BOTTOM (zentriert) (rechtsbündig) (linksbündig) (obenbündig) (untenbündig) ◇ Im Unterschied zu JLabel-Objekten können JButton-Objekte (Schaltknöpfe !) vom Benutzer veranlasste Ereignisse auslösen. Ein Schaltknopf kann auch den Focus besitzen und dann auch Tastatur-Eingaben empfangen. Ein Schaltknopf kann vom Benutzer – z.B. durch einen Mausklick oder bei vorhandenen Focus durch die Leertaste – betätigt ("gedrückt") werden. Ein "gedrückter" Schaltknopf ändert seine Hintergrundfarbe. ◇ Konstruktoren (Auswahl) public JButton() Erzeugung eines JButton-Objekts ohne "Inhalt" public JButton(String text) Erzeugung eines JButton-Objekts das mit dem Text text beschriftet ist (horizontal rechtsbündig ausgerichtet) public JButton(Icon image) Erzeugung eines JButton-Objekts, das mit dem Bild image versehen ist (horizontal rechtsbündig ausgerichtet) public JButton(String text, Icon image) Erzeugung eines JButton-Objektsd, das mit dem Text text beschriftet und mit dem Bild image versehen ist ◇ Der überwiegende Teil der von der Klasse JButton angebotenen Schnittstelle ist geerbt von der Klasse AbstractButton. Teilweise werden die gleichen Memberfunktionen – mit gleicher Funktionalität – wie bei der Klasse JLabel zur Verfügung gestellt Memberfunktionen (von AbstractButton geerbt, Auswahl) public String getText() Rückgabe des dargestellten Textes public Icon getIcon() Rückgabe des dargestellten Bildes public void setText(String text) Setzen des darzustellenden Textes auf text public void setIcon(Icon image) Setzen des darzustellenden Bildes auf image public void setHorizontalAlignment(int halign) Setzen der horizontalen Ausrichtung gemäß halign (für Text und Bild) public void setVerticalAlignment(int valign) Setzen der vertikalen Ausrichtung gemäß valign (für Text und Bild) 30 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 566 – 00 – TH – 05 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (6) Die Klasse JTextField (Package javax.swing) ◇ Diese Klasse ist von der abstrakten Basisklasse JTextComponent (Package javax.swing.text) abgeleitet. Sie dient zur Erzeugung von editierbaren einzeiligen Textfeldern. Die Editierbarkeit kann ein- und ausgeschaltet werden (Default nach Objekt-Erzeugung : eingeschaltet). ◇ JTextField-Objekte können sowohl zur Ausgabe als auch zur Eingabe (wenn die Editierbarkeit eingeschaltet ist) von Text verwendet werden. ◇ Die Größe eines Textfeldes (= Anzahl der dargestellten Zeichenpositionen) kann kleiner (und natürlich auch größer) als die Textlänge sein. Das heisst, dass auch Text eingegeben werden kann, desen Länge die Anzahl der darstellbaren Positionen überschreitet. ◇ Die horizontale Ausrichtung des Textes innerhalb des dargestellten Zeilenfensters kann festgelegt werden. Hierfür stehen die folgenden Konstanten zur Verfügung : - JTextField.LEFT (linksbündig) - JTextField.RIGHT (rechtsbündig) - JTextField.CENTER (zentriert) - JTextField.LEADING (Darstellung der führenden Zeichen, wenn Feldlänge kleiner als Textlänge, default) - JTextField.TRAILING (Darstellung der Zeichen am Ende, wenn Feldlänge kleiner als Textlänge) ◇ Die Editierfunktionalität umfasst auch die Zusammenarbeit mit dem Clipboard : Text kann aus dem Clipboard eingefügt bzw im Textfeld markiert und – ausgeschnitten oder kopiert – in das Clipboard eingefügt werden. ◇ Konstruktoren (Auswahl) public JTextField() Erzeugung eines "leeren" JTextField-Objekts, die Feldgröße = 0 public JTextField(String text) Erzeugung eines JTextField-Objekts, das mit text initialisiert ist, die Feldgröße wird durch die Länge von text bestimmt. public JTextField(int cols) Erzeugung eines "leeren" JTextField-Objekts, die Feldgröße ist durch cols festgelegt. public JTextField(String text, Erzeugung eines JTextField-Objekts, das mit text initialisiert ist, int cols) die Feldgröße ist durch cols festgelegt ◇ Memberfunktionen (Auswahl) Die meisten der nachfolgend aufgeführten Memberfunktionen sind von der Klasse JTextComponent geerbt. public void setColumns(int cols) Setzen der Feldgröße auf cols, das Layout wird ungültig gesetzt public String getText() *) Rückgabe des im Textfeld enthaltenen Textes public String getSelectedText() *) Rückgabe des markierten Teils des im Textfeld enthaltenen Textes public void setText(String text) *) Setzen des im Textfeld enthaltenen Textes auf text public void setEditable(boolean b) *) Setzen / Aufheben der Editierbarkeit des Textfeldes gemäß b b==true : Setzen der Editierbarkeit, andernfalls Aufheben public boolean isEditable() Ermittelung der Editierbarkeit des Textfeldes Rückgabewert ==true : editierbar, andernfalls nicht editierbar *) public void setHorizontalAlignment(int halign) *) geerbt von JTextComponent (Package javax.swing.text) 31 Setzen der horizontalen Ausrichtung des Textfeldes auf halign FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 567 – 01 – TH – 02 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (7-1) Die Klasse JTextArea (Package javax.swing) ◇ Diese Klasse ist ebenfalls von der Klasse JTextComponent abgeleitet. Sie dient zur Darstellung editierbarer mehrzeiliger Textfelder. Wie bei der Klasse JTextField kann die Editierbarkeit ein- und ausgeschaltet werden (Default nach ObjektErzeugung : eingeschaltet). ◇ JTextArea-Objekte können sowohl zur Ausgabe als auch zur Eingabe (bei eingeschalteter Editierbarkeit) von Text, der mehrere Zeilen umfassen kann, eingesetzt werden. Die Größe des im JTextArea-Objektes darzustellenden Textes kann sich damit dynamisch ändern. ◇ Auch bei dieser Klasse schliesst die Editierfunktionalität die Verwendung des Clipboards ein : Text kann aus dem Clipboard eingefügt bzw im Textfeld markiert und – ausgeschnitten oder kopiert – in das Clipboard eingefügt werden. ◇ Die Klasse JTextArea stellt mehrere Methoden zur Verfügung, mit denen der dargestellte Text auch vom Programm aus editiert werden kann. U.a. existieren Methoden zum zeilenweisen Text-Zugriff sowie zum Einfügen und Anhängen von Text. ◇ Die Größe des im JTextArea-Objektes darzustellenden Textes kann sich infolge des benutzer-initiierten oder programm-initiierten Editierens dynamisch ändern. ◇ Es ist möglich, für ein JTextArea-Objekt die Anzahl der Zeilen und Spalten festzulegen. Diese dienen zur Ermittlung seiner bevorzugten Größe (preferred size), die aber nicht der Größe des tatsächlich dargestellten Textfensters (Darstellungsbereich) entsprechen muß. Diese wird vielmehr durch den eingesetzten Layout-Manager gegebenenfalls unter Berücksichtigung der Größe des umfassenden Containers festgelegt. ◇ Für den Fall, dass eine darzustellende Zeile länger als die dargestellte Textfenster-Breite ist, kann ein automatischer Zeilenumbruch eingeschaltet werden Bei eingeschalteten Zeilenumbruch kann festgelegt werden, ob ein Umbruch nach jedem Zeichen (zeichenweiser Umbruch) oder nur an einer Wortgrenze (wortweiser Umbruch) erfolgen kann. Defaultmässig ist der Zeilenumbruch ausgeschaltet. Der nicht in das dargestellte Fenster passende Zeilenteil ist dann nicht sichtbar. Analoges gilt, wenn die tatsächliche Anzahl der darzustellenden Textzeilen größer als die durch das dargestellte Textfenster gegebene Zeilenzahl ist. ◇ JTextArea-Objekte besitzen keine Scroll-Fähigkeiten (Unterschied zu der AWT-Klasse TextArea). Diese lassen sich aber durch das Einbetten eines JTextArea-Objektes in ein JScrollPane-Objekt realisieren. In diesem Fall wird für den Scrollbereich die durch Zeilen- und Spaltenzahl festgelegte bevorzugte Größe berücksichtigt. ◇ Konstruktoren (Auswahl) public JTextArea() Erzeugung eines "leeren" JTextArea-Objekts (Die Referenz auf den enthaltenen String ist null) Die Werte für die Anzahl der Zeilen und Spalten werden auf 0 gesetzt public JTextArea(String text) Erzeugung eines JTextArea-Objekts, das mit text initialisiert ist, Die Werte für die Anzahl der Zeilen und Spalten werden auf 0 gesetzt public JTextArea(int rows, int cols) Erzeugung eines "leeren" JTextArea-Objekts, Die Anzahl der Zeilen ist durch rows, die Anzahl der Spalten durch cols festgelegt. public JTextArea(String text, int rows, int cols) Erzeugung eines JTextArea-Objekts, das mit text initialisiert ist, Die Anzahl der Zeilen ist durch rows, die Anzahl der Spalten durch cols festgelegt. 32 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 567 – 02 – TH – 02 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (7-2) Die Klasse JTextArea (Package javax.swing), Forts. ◇ Memberfunktionen (Auswahl) Wie in der Klasse JTextField werden zahlreiche Memberfunktionen von der Klasse JTextComponent geerbt. Zusätzlich definiert die Klasse JTextArea eine Reihe eigener Methoden public String getText() *) Rückgabe des im Textfeld enthaltenen Textes public String getSelectedText() *) Rückgabe des markierten Teils des im Textfeld enthaltenen Textes public void setText(String text) *) Setzen des im Textfeld enthaltenen Textes auf text public void setEditable(boolean b) *) Setzen / Aufheben der Editierbarkeit des Textfeldes gemäß b b==true : Setzen der Editierbarkeit, andernfalls Aufheben public boolean isEditable() Ermittlung der Editierbarkeit des Textfeldes Rückgabewert ==true : editierbar, andernfalls nicht editierbar *) public void setRows(int rows) Setzen der Anzahl Zeilen auf rows public void setColumns(int cols) Setzen der Anzahl Spalten auf cols public int getRows() Ermittlung der festgelegten Zeilen-Anzahl public int getColumns() Ermittlung der festgelegten Spalten-Anzahl public int getLineCount() Ermittlung der tatsächlich im Text vorhandenen Zeilenzahl public void setLineWrap(boolean b) Setzen/Aufheben des automatischen Zeilenumbruchs gemäß b b==true : Setzen des Zeilenumbruchs, andernfalls Aufheben public boolean getLineWrap() Ermittlung, ob automatischer Zeilenumbruch gesetzt ist Rückgabewert ==true : Zeilenumbruch ist gesetzt public void setWrapStyleWord(boolean b) Setzen der Art des Zeilenumbruchs gemäß b b==true : wortweiser Zeilenumbruch b==false : zeichenweiser Zeilenumbruch (Default) public boolean getWrapStyleWord() Ermittlung der Art des Zeilenumbruchs Rückgabewert ==true : wortweiser Zeilenumbruch Rückgabewert ==false : zeichenweiser Zeilenumbruch void append(String str) Anhängen des Strings str an das Ende des dargestellten Textes void insert(String str, int pos) Einfügen des Strings str an der Position pos im Text void replaceRange(String str, int beg, int end) Ersetzen des Textes zwischen den Positionen beg und end durch den String str *) geerbt von JTextComponent (Package javax.swing.text) 33 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 567 – 03 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (7-3) Demonstrationsprogramm zur Klasse JTextArea (JTextAreaDemo) // JTextAreaDemo.java import java.awt.*; import javax.swing.*; import java.io.*; public class JTextAreaDemo extends JFrame { private final static String DEF_FILE_NAME = "schlechterwitz.txt"; private Container c; private JTextField tf; private JTextArea ta; public JTextAreaDemo(String dname) { super("JTextAreaDemo"); tf = new JTextField("Inhalt der Datei \"" + dname + '\"'); tf.setHorizontalAlignment(JTextField.CENTER); tf.setFont(new Font("SansSerif", Font.BOLD, 14)); tf.setBackground(Color.YELLOW); c=getContentPane(); ta = new JTextArea(20, 50); if (dname!=null) fillTextAreaFromFile(dname); ta.setFont(new Font("SansSerif", Font.PLAIN, 13)); //ta.setLineWrap(true); // Setzen des automatischen Zeilenumbruchs //ta.setWrapStyleWord(true); // Setzen der Umbruchsart auf wortweise c.add(tf, BorderLayout.NORTH); c.add(ta); setSize(360, 270); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void fillTextAreaFromFile(String dname) { try { BufferedReader bfr = new BufferedReader(new FileReader(dname)); String line; while ((line=bfr.readLine()) != null) ta.append(line + '\n'); bfr.close(); } catch(IOException ex) { System.out.println("Exception " + ex.getMessage()); } } public static void main(String[] args) { String fname; if (args.length==0) fname = DEF_FILE_NAME; else fname = args[0]; new JTextAreaDemo(fname).setVisible(true); } } 34 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 567 – 04 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (7-4) Ausgabe des Demonstrationsprogramms zur Klasse JTextArea (JTextAreaDemo) ◇ Automatischer Zeilenumbruch nicht gesetzt (default) ◇ Automatischer Zeilenumbruch gesetzt (zeichenweiser Umbruch) ◇ Automatischer Zeilenumbruch gesetzt (wortweiser Umbruch) 35 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 568 – 01 – TH – 03 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (8-1) Die Klasse JScrollPane (Package javax.swing) ◇ Objekte dieser Klasse dienen zur Einbettung anderer GUI-Komponenten in einen scrollbaren Darstellungsbereich. Statt die GUI-Komponente direkt in einen Container einzufügen, wird sie in ein JScrollPane-Objekt eingebettet und dieses dann in den Container eingefügt. ◇ JScrollPane-Objekte verwalten einen Darstellungsbereich (viewport), in dem die eingebettete GUI-Komponente eingeblendet wird. Wenn die eingebettete Komponente größer als der zur Verfügung stehende Darstellungsbereich ist, wird sie nur ausschnittsweise angezeigt. Mit Hilfe von zwei Schiebereglern (scroll bars, horizontale und vertikale Bildlaufleiste) kann der dargestellte Ausschnitt dynamisch verändert werden. ◇ Es kann festgelegt werden, wann die Bildlaufleisten sichtbar sein sollen (Scroll Bar Policy) : ▻ nur sichtbar, wenn nötig (default) ▻ immer sichtbar ▻ nie sichtbar Dies kann – getrennt für die vertikale und horizontale Bildlaufleiste – im Konstruktor bei der JScrollPane-ObjektErzeugung oder später mittels spezieller Memberfunktionen erfolgen. Zur Festlegung stehen die folgenden – im implementierten Interface javax.swing) definierten – Konstanten zur Verfügung : - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS - JScrollPane.VERTICAL_SCROLLBAR_NEVER - JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ScrollPaneConstants (Package (vertikale Bildlaufleiste nur bei Bedarf sichtbar) (horizontale Bildlaufleiste nur bei Bedarf sichtbar) (vertikale Bildlaufleiste immer sichtbar) (horizontale Bildlaufleiste immer sichtbar) (vertikale Bildlaufleiste nie sichtbar) (horizontale Bildlaufleiste nie sichtbar) ◇ Prinzipiell kann jede beliebige GUI-Komponente in ein JScrollPane-Objekt eingebettet werden. Diese kann im Konstruktor angegeben oder mittels einer entsprechenden Memberfunktion festgelegt werden. ◇ Für ein JScrollPane-Objekt können zusätzlich ein Zeilen-Header, ein Spalten-Header und Eck-Komponenten festgelegt werden. ◇ Konstruktoren (Auswahl) public JScrollPane() Erzeugung eines "leeren" JScrollPane-Objekts. Beide Bildlaufleisten werden nur bei Bedarf dargestellt public JScrollPane(Component view) Erzeugung eines JScrollPane-Objekts, in dem das GUIKomponenten-Objekt view eingebettet ist. Beide Bildlaufleisten werden nur bei Bedarf dargestellt public JScrollPane(Component view, int vsbPol, int hsbPol) Erzeugung eines JScrollPane-Objekts, in dem das GUIKomponenten-Objekt view eingebettet ist Die Sichtbarkeit der Bildlaufleisten wird durch vsbPol (vertikal und hsbPol (horizontal) festgelegt ◇ Memberfunktionen (Auswahl) public void setVerticalScrollBarPolicy(int pol) Setzen der ScrollBarPolicy für die vertikale Bildlaufleiste auf pol public void setHorizontalScrollBarPolicy(int pol) Setzen der ScrollBarPolicy für die horizontale Bildlaufleiste auf pol public void setViewportView(Component view) 36 Einbettung des GUI-Komponenten-Objekts view FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 568 – 02 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (8-2) Demonstrationsprogramm zur Klasse JScrollPane (JScrollPaneDemo) // JScrollPaneDemo.java // Einbettung eines JTextArea-Objekts in ein JScrollPane-Objekt // Modifikation des Demonstrations-Programms zur Klasse JTextArea import java.awt.*; import javax.swing.*; import java.io.*; public class JScrollPaneDemo extends JFrame { final static String DEF_FILE_NAME = /*"beispiel.txt"*/"schlechterwitz.txt"; private Container c; private JTextField tf; private JTextArea ta; private JScrollPane sp; public JScrollPaneDemo(String dname) { super("JScrollPaneDemo"); tf = new JTextField("Inhalt der Datei \"" + dname + '\"'); tf.setHorizontalAlignment(JTextField.CENTER); tf.setFont(new Font("SansSerif", Font.BOLD, 14)); tf.setBackground(Color.YELLOW); c=getContentPane(); ta = new JTextArea(20,50); if (dname!=null) fillTextAreaFromFile(dname); ta.setFont(new Font("SansSerif", Font.PLAIN, 13)); sp = new JScrollPane(ta); c.add(tf, BorderLayout.NORTH); c.add(sp); setSize(380, 250); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void fillTextAreaFromFile(String dname) { try { BufferedReader bfr = new BufferedReader(new FileReader(dname)); String line; while ((line=bfr.readLine()) != null) ta.append(line + '\n'); bfr.close(); } catch(IOException ex) { System.out.println("Exception " + ex.getMessage()); } } public static void main(String[] args) { String fname; if (args.length==0) fname = DEF_FILE_NAME; else fname = args[0]; new JScrollPaneDemo(fname).setVisible(true); } } 37 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 568 – 03 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (8-3) Ausgabe des Demonstrationsprogramms zur Klasse JScrollPane (JScrollPaneDemo) ◇ Automatischer Zeilenumbruch für das eingebettete JTextArea-Objekt nicht gesetzt (default) ◇ Automatischer Zeilenumbruch für das eingebettete JTextArea-Objekt gesetzt (wortweiser Umbruch) ◇ Automatischer Zeilenumbruch für das eingebettete JTextArea-Objekt nicht gesetzt (default) 38 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 568 – 04 – TH – 02 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (8-4) Weiteres Demonstrationsprogramm zur Klasse JScrollPane (JScrollPaneDemo2) // JScrollPaneDemo2.java // Einbettung eines JButton-Objekts mit einem Bild in ein JScrollPane-Objekt import java.awt.*; import javax.swing.*; import java.io.*; public class JScrollPaneDemo2 extends JFrame { final static String DEF_FILE_NAME = "UnixCountry.jpg"; private Container c; private JButton but; // alternativ : JLabel but; private JTextField tf; private JScrollPane sp; public JScrollPaneDemo2(String dname) { super("JScrollPaneDemo2"); tf = new JTextField("Inhalt der Datei \"" + dname + '\"'); tf.setHorizontalAlignment(JTextField.CENTER); tf.setFont(new Font("SansSerif", Font.BOLD, 14)); tf.setBackground(Color.YELLOW); c=getContentPane(); Icon pict = new ImageIcon(dname); but = new JButton(pict); // alternativ : but = new JLabel(pict); sp = new JScrollPane(but); c.add(tf, BorderLayout.NORTH); c.add(sp); setSize(550, 350); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { String fname; if (args.length==0) fname = DEF_FILE_NAME; else fname = args[0]; new JScrollPaneDemo2(fname).setVisible(true); } } 39 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 568 – 05 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (8-5) Ausgabe des weiteren Demonstrationsprogramms zur Klasse JScrollPane (JScrollPaneDemo2) ◇ verschiedene Stellungen der Schieberegler (Bildlaufleiste) 40 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 569 – 00 – TH – 02 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (9) Die Klasse JCheckBox (Package javax.swing) ◇ Diese Klasse dient zur Erzeugung von Auswahlfeldern. Ein Auswahlfeld (check box) ist ein kleines Kästchen, das bei der Anwahl selektiert und deselektiert werden kann. Der jeweilige Selektionszustand wird angezeigt : De-selektiert ist das Kästchen leer, selektiert enthält es einen kleinen Haken. ◇ Ein JCheckBox-Objekt dient i.a. zur Darstellung eines logischen Wertes (boolean) auf einer GUI-Oberfläche. ◇ In einer Gruppe von JCheckBox-Objekten (Auswahlfeldern) können beliebig viele selektiert sein ◇ Ein JCheckBox-Objekt kann mit einem Text beschriftet oder/und mit einem Bild versehen werden. Ein Bild ersetzt dabei das Auswahlfeld-Kästchen in der Darstellung. Eine eventuelle Selektion kann dann optisch nicht mehr erkannt werden. ◇ Die Klasse ist von der Klasse JToggleButton abgeleitet, die ihrerseits die abstrakte Klasse AbstractButton als direkte Basisklasse hat. ◇ Konstruktoren (Auswahl) public JCheckBox() Erzeugung eines nicht selektierten JCheckBox-Objekts ohne Text und ohne Bild public JCheckBox(String text) Erzeugung eines nicht selektierten JCheckBox-Objekts, mit dem Text text beschriftet public JCheckBox(String text, boolean select) Erzeugung eines JCheckBox-Objekts, dessen AnfangsSelektionszustand durch select festgelegt ist (select==true : selektiert), Beschriftung mit Text text public JCheckBox(Icon image) Erzeugung eines nicht selektierten JCheckBox-Objekts, mit dem Bild image versehen public JCheckBox(Icon image, boolean select) Erzeugung eines JCheckBox-Objekts, dessen AnfangsSelektionszustand durch select festgelegt ist (select==true : selektiert), mit dem Bild image versehen public JCheckBox(String text, Icon image) Erzeugung eines nicht selektierten JCheckBox-Objekts, mit dem Text text beschriftet und dem Bild image versehen public JCheckBox(String text, Icon image, boolean select) Erzeugung eines JCheckBox-Objekts, dessen AnfangsSelektionszustand durch select festgelegt ist (select==true : selektiert), mit dem Text text beschriftet und mit dem Bild image versehen ◇ Memberfunktionen : Ein großer Teil der angebotenen Schnittstelle ist von der Klasse AbstractButton geerbt, u.a. : public void setSelected(boolean b) Setzen des Selektionszustands gemäß b b==true : selektiert, andernfalls nicht selektiert public boolean isSelected() Ermittlung des Selektionszustands Rückgabewert ==true : selektiert, ==false : nicht selektiert 41 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56A – 01 – TH – 04 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (10-1) Die Klasse JRadioButton (Package javax.swing) ◇ Diese Klasse dient ebenfalls zur Erzeugung von Auswahlfeldern. JRadioButton-Objekte können somit auch zur Darstellung logischer Werte (boolean) auf einer GUI-Oberfläche eingesetzt werden. ◇ Im Unterschied zur Klasse JCheckBox werden aber mehrere JRadioButton-Objekte meist so zu einer Gruppe zusammengefasst, dass immer nur ein Auswahlfeld aus der Gruppe selektiert werden soll (und kann) (Gruppe alternativer Auswahlfelder). ◇ Ein Auswahlfeld der Klasse JRadioButton wird durch einen kleinen Kreis dargestellt. Im unselektierten Zustand ist der Kreis leer, bei Selektion erscheint ein Punkt im Kreis. ◇ Die Klasse JRadioButton ist ebenfalls von der Klasse JToggleButton und damit auch von der abstrakten Klasse AbstractButton abgeleitet. ◇ Auch ein JRadioButton-Objekt kann mit einem Text beschriftet oder/und mit einem Bild versehen werden. Ein Bild ersetzt dabei den Auswahlfeld-Kreis in der Darstellung. Eine eventuelle Selektion kann dann optisch nicht mehr erkannt werden. ◇ Konstruktoren (Auswahl) public JRadioButton() Erzeugung eines nicht selektierten JRadioButton-Objekts ohne Text und ohne Bild public JRadioButton (String text) Erzeugung eines nicht selektierten JRadioButton -Objekts, mit dem Text text beschriftet public JRadioButton (String text, Erzeugung eines JRadioButton -Objekts, dessen Anfangsboolean select) Selektionszustand durch select festgelegt ist (select==true : selektiert), Beschriftung mit Text text public JRadioButton (Icon image) Erzeugung eines nicht selektierten JRadioButton -Objekts, mit dem Bild image versehen public JRadioButton (Icon image, Erzeugung eines JRadioButton -Objekts, dessen Anfangsboolean select) Selektionszustand durch select festgelegt ist (select==true : selektiert), mit dem Bild image versehen public JRadioButton (String text, Icon image) Erzeugung eines nicht selektierten JRadioButton -Objekts, mit dem Text text beschriftet und dem Bild image versehen public JRadioButton (String text, Erzeugung eines JRadioButton -Objekts, dessen AnfangsIcon image, Selektionszustand durch select festgelegt ist boolean select) (select==true : selektiert), mit dem Text text beschriftet und mit dem Bild image versehen ◇ Memberfunktionen : Die angebotene Schnittstelle entspricht weitgehend – soweit sie von der Klasse AbstractButton geerbt ist – der Schnittstelle der Klasse JCheckBox. U.a. stehen somit auch die folgenden Methoden zur Verfügung : public void setSelected(boolean b) Setzen des Selektionszustands gemäß b b==true : selektiert, andernfalls nicht selektiert public boolean isSelected() Ermittlung des Selektionszustands Rückgabewert ==true : selektiert, ==false : nicht selektiert 42 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56A – 02 – TH – 03 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (10-2) Die Klasse ButtonGroup (Package javax.swing) ◇ Objekte dieser Klasse dienen zur logischen Zusammenfassung mehrerer JRadioButton-Objekte zu einer Gruppe, in der immer nur ein Objekt selektiert werden kann. ◇ Sie stellen selbst keine GUI-Komponenten dar und erscheinen deshalb auch nicht auf der GUI-Oberfläche. ◇ Nach der Erzeugung eines ButtonGroup-Objekts und dem Hinzufügen der JRadioButton-Objekte, die zusammengefasst werden sollen, sind alle JRadioButton-Objekte zunächst de-selektiert. Nach der erstmaligen Selektion eines JRadioButton-Objekts ist immer genau ein Objekt der Gruppe selektiert. Es gibt danach keine Möglichkeit mehr, alle Objekte gleichzeitig in den de-selektierten Zustand zu versetzen. ◇ Konstruktor : public ButtonGroup() Erzeugung eine neuen ButtonGroup-Objekts ◇ Memberfunktionen : public void add(AbstractButton b) Hinzufügen des Buttons (konkret JRadioButton-Objekts) b zur Gruppe public void remove(AbstractButton b) Entfernen des Buttons (konkret JRadioButton-Objekts) b aus der Gruppe public int getButtonCount() Rückgabe der Anzahl in der Gruppe enthaltenen AbstractButton-(konkret JRadioButton-)Objekte public Enumeration<AbstractButton> getElements() Rückgabe aller in der Gruppe enthaltenen AbstractButton(konkret JRadioButton-)Objekte ◇ Anmerkung : ▻ Genaugenommen können durch ein ButtonGroup-Objekt nicht nur JRadioButton-Objekte sondern Objekte jeder von AbstractButton abgeleiteten Klasse zusammengefasst werden (Parameter von add() !!!). Allerdings ist eine derartige Zusammenfassung nicht immer sinnvoll. ▻ Beispielsweise lassen sich auch JCheckBox-Objekte derartig zu einer Gruppe zusammenfassen. Diese Gruppe ver hält sich dann wie eine JRadioButton-Gruppe (nur eine Checkbox kann jeweils alternativ selektiert werden), was aber der typischen Anwendung von JCheckBox-Objekten wiederspricht. ▻ Bei Objekten einiger von AbstractButton abgeleiteten Klassen (z.B. JButton und JMenuItem) macht die Zusammenfassung zu einer Gruppe schon deshalb keinen Sinn, weil sie den jeweiligen Selektionszustand nicht anzeigen. 43 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 56A – 03 – TH – 01 ----------------------------------------------------------------------------------- Ausgewählte Swing-Komponenten-Klassen (10-3) Demonstrationsprogramm zu den Klassen JCheckBox und JRadioButton // AuswahlfeldDemo.java import java.awt.*; import javax.swing.*; public class AuswahlfeldDemo extends JFrame { private Container c; private JCheckBox[] cba; private JRadioButton[] rba; public AuswahlfeldDemo(int anz) { setTitle("AuswahlfeldDemo"); c = getContentPane(); c.setLayout(new GridLayout(2,0)); cba = new JCheckBox[anz]; for (int i=0; i<cba.length; i++) { cba[i] = new JCheckBox("Auswahl " + (i+1)); c.add(cba[i]); } rba = new JRadioButton[anz]; ButtonGroup bg = new ButtonGroup(); for (int i=0; i<rba.length; i++) { rba[i] = new JRadioButton("Select " + (i+1)); bg.add(rba[i]); c.add(rba[i]); } setSize(380, 120); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { new AuswahlfeldDemo(4).setVisible(true); } } 44 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56B – 01 – TH – 05 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (11-1) Die Klasse JPanel (Package javax.swing) ◇ Diese Klasse dient zur Erzeugung von Gruppierungsfeldern. Ein Gruppierungsfeld ist ein "innerer" Container, der in erster Linie zur strukturierten Gestaltung des Inhalts von Fensterbereichen dient. Mit einem Gruppierungsfeld lassen sich mehrere Komponenten zu einer Komponente zusammenfassen, die dann wiederum in einen anderen Container (Top-Level-Container oder ein weiterer "innerer" Container) eingefügt werden kann. ◇ Die Klasse JPanel ist von der Klasse JComponent abgeleitet. ◇ Wie andere Container auch, verwenden JPanel-Objekte einen Layout-Manager. Im Unterschied zu den Top-LevelContainern ist bei ihnen Flow-Layout als Default eingestellt. Ein anderes Layout lässt sich gegebenenfalls bei der Objekt-Erzeugung im Konstruktor oder mittels der – von der Klasse Container geerbten – Methode setLayout() setzen. Allerdings ist ein Setzen des BoxLayouts bei der Objekterzeugung ist nicht möglich. Die folgende Anweisung JPanel mpan = new JPanel(new BoxLayout(mpan, BoxLayout.Y_AXIS)); führt zur der Compiler-Fehlermeldung "Variable mpan ist nicht initialisiert worden". ◇ JPanel-Objekte verfügen von Haus aus über keine Umrandung. Bei Bedarf lassen sich aber Umrandungen mit der von der Klasse JComponent geerbten Methode setBorder() setzen. ◇ Konstruktoren (Auswahl) public JPanel() Erzeugung eines JPanel-Objekts (Flow-Layout voreingestellt) public JPanel(LayoutManager layout) Erzeugung eines JPanel-Objekts mit dem durch layout festgelegten Layout-Manager ◇ Memberfunktionen Die Klasse JPanel stellt die Methoden ihrer direkten und indirekten Basisklassen JComponent, Container und Component zur Verfügung. Sie fügt nur wenige weitere – für die Anwendung i.a. nicht wesentliche – Methoden hinzu. 45 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56B – 02 – TH – 03 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (11-2) Demonstrationsprogramm zur Klasse JPanel import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class JPanelDemo extends JFrame { Container c; JPanel pan1, pan2, pan3; public JPanelDemo() { super("JPanelDemo"); c=getContentPane(); pan1 = new JPanel(); pan2 = new JPanel(); pan3 = new JPanel(new GridLayout(2,3)); for (int i=1; i<=4; i++) pan1.add(new JButton("Button " + i)); pan1.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); pan1.setBackground(Color.ORANGE); Icon javaLogo = new ImageIcon("javalogo52x88.gif"); JLabel lab1 = new JLabel(javaLogo); JLabel lab2 = new JLabel("Java", SwingConstants.LEFT); lab2.setBorder(BorderFactory.createEmptyBorder(0,0,0,30)); JLabel lab3 = new JLabel("avaJ", SwingConstants.RIGHT); lab3.setBorder(BorderFactory.createEmptyBorder(0,30,0,0)); pan2.add(lab2); pan2.add(lab1); pan2.add(lab3); for (int i=1; i<=6; i++) pan3.add(new JCheckBox("Auswahl " + i)); Border bord1 = BorderFactory.createLineBorder(Color.GREEN, 5); Border bord2 = BorderFactory.createTitledBorder(bord1, "Auswahl"); pan3.setBorder(bord2); c.add(pan1, BorderLayout.NORTH); c.add(pan2, BorderLayout.CENTER); c.add(pan3, BorderLayout.SOUTH); setSize(380, 270); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { new JPanelDemo().setVisible(true); } } 46 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56C – 01 – TH – 01 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (12-1) Die Klasse Box (Package javax.swing) ◇ Diese – direkt von JComponent abgeleitete – Klasse ist eine weitere "innere" Container-Klasse. Sie dient ebenfalls zur Erzeugung von Gruppierungsfeldern. ◇ Defaultmässig ist Box-Layout eingestellt. Dieses Layout kann jedoch nicht verändert werden Die – indirekt von Container geerbte – Methode setLayout(...) ist so überschrieben, dass sie immer eine AWTError-Exception wirft. Gruppierungs-Box. ◇ Die Anordnungs-Achse des verwendeten Box-Layouts (vertikal oder horizontal) ist bei der Objekt-Erzeugung anzugeben. ◇ Bezüglich der Verwendung als Gruppierungsfelder können Box-Objekte im wesentlichen wie JPanel-Objekte eingesetzt werden. Auch Box-Objekte verfügen von Haus aus über keine Umrandung. Mittels der von JComponent geerbten Methode setBorder(...) lassen sich jederzeit gewünschte Umrandungen setzen. ◇ Als zusätzliche Besonderheit stellt die Klasse Box eine Reihe von statischen Methoden zur Verfügung, mit denen unsichtbare GUI-Komponenten erzeugt werden können, die sich als Abstands- und Füll-Komponenten im BoxLayout – auch in Nicht-Box-Containern – einsetzen lassen. Diese Komponenten sind Objekte der Klasse Box.Filler (innere Klasse von Box), die ebenfalls direkt von der Klasse JComponent abgeleitet ist. Prinzipiell können diese Komponenten auch in anderen Layouts verwendet werden, aber ihre wesentliche Bedeutung haben sie beim BoxLayout. ◇ Konstruktor public Box(int axis) Erzeugung eines Box-Objekts dessen Layout, die durch axis festgelegte Anordnungs-Achse besitzt. Folgende Konstante sind zulässige Werte für axis : - BoxLayout.X_AXIS (horizontale Anordnung, in einer Zeile) - BoxLayout.Y_AXIS (vertikale Anordnung, in einer Spalte) - BoxLayout.LINE_AXIS (für europäische Sprachen : horizontale Anordnung) - BoxLayout.PAGE_AXIS (für europäische Sprachen : vertikale Anordnung) ◇ Statische Methoden zur Objekterzeugung public static Box createHorizontalBox() Erzeugung eines Box-Objekts mit horizontaler Anordnungs-Achse seines Layouts public static Box createVerticalBox() Erzeugung eines Box-Objekts mit vertikaler Anordnungs-Achse seines Layouts ◇ Statische Methoden zur Erzeugung unsichtbarer GUI-Komponenten (Auswahl) public static Component createRigidArea(Dimension d) Erzeugung eines Box.Filler-Objekts das die durch d definierte feste Grösse besitzt ( Abstands-Komponente) public static Component createHorizontalGlue() Erzeugung eines Box.Filler-Objekts variabler anpassbarer Breite ( horizontale Füll-Komponente) public static Component createVerticalGlue() 47 Erzeugung eines Box.Filler-Objekts variabler anpassbarer Höhe ( vertikale Füll-Komponente) FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56C – 03 – TH – 01 ------------------------------------------------------------------------------------ Ausgewählte Swing-Komponenten-Klassen (12-3) Demonstrationsprogramm zur Klasse Box (und zu unsichtbaren GUI-Komponenten) import javax.swing.*; import javax.swing.border.*; import java.awt.*; public class BoxDemo extends JFrame { public BoxDemo() { super("BoxDemo"); Box mbox = new Box(BoxLayout.Y_AXIS); Box box1 = Box.createHorizontalBox(); // = new Box(BoxLayout.X_AXIS); Box box2 = Box.createVerticalBox(); // = new Box(BoxLayout.Y_AXIS); mbox.setBorder(BorderFactory.createLineBorder(Color.BLACK,3)); box1.setBorder(BorderFactory.createLineBorder(Color.GREEN,3)); box2.setBorder(BorderFactory.createLineBorder(Color.RED,3)); box1.setAlignmentX(CENTER_ALIGNMENT); box2.setAlignmentX(CENTER_ALIGNMENT); for (int i=1; i<=3; i++) { box1.add(Box.createRigidArea(new Dimension(5,0))); box1.add(new JButton("HB-Button " + i)); } box1.add(Box.createHorizontalGlue()); box1.setMinimumSize(new Dimension(400, 60)); box1.setMaximumSize(box1.getMinimumSize()); box1.setPreferredSize(box1.getMinimumSize()); box2.add(Box.createVerticalGlue()); for (int i=1; i<=2; i++) { box2.add(new JButton("VB-Button " + i)); box2.add(Box.createRigidArea(new Dimension(0,5))); } mbox.add(Box.createRigidArea(new Dimension(0,10))); mbox.add(box1); mbox.add(Box.createRigidArea(new Dimension(0,10))); mbox.add(box2); mbox.add(Box.createRigidArea(new Dimension(0,10))); add(mbox, BorderLayout.CENTER); setSize(480, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { new BoxDemo().setVisible(true); } } 48 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 571 – 00 – TH – 02 ------------------------------------------------------------------------------------ Ereignisverarbeitung in JFC-GUI-Komponenten (1) Delegation Event Model ◇ In Programmen mit einem grafischen Benutzerinterface findet die Kommunikation zwischen dem Benutzer und dem Programm mittels Ereignissen (events) statt. ◇ Benutzerinteraktionen mit der graphischen Oberfläche (wie z.B. Anwählen eines Schaltknopfes oder Auswahl eines Menue-Eintrags mittels Maus oder Tastatur, Schliessen eines Fensters, sonstige Mausbewegungen und Mausklicks, sonstige Tastatureingaben usw) lösen Ereignisse aus, die als Nachrichten vom Betriebssystem an das Programm gesandt werden. Auch Änderungen der Größe, der Lage, des Inhalts und des sonstigen Zustands von GUI-Komponenten können zu Ereignissen in diesem Sinne führen. Ereignisse können also von unterschiedlichem Typ sein. ◇ Innerhalb eines Java-Programms werden die durch Nachrichten angezeigten Ereignisse als Objekte spezieller EreignisKlassen (event objects) dargestellt. Diese Ereignis-Objekte kapseln Informationen über das jeweilige Ereignis (z.B. die Ereignisquelle, spezielle Parameter, wie Mauskoordinaten, Tastaur-Codes usw) ◇ Die GUI-Komponente an der oder durch die ein Ereignis erzeugt wurde, wird als Ereignisquelle (event source) bezeichnet. Prinzipiell kann jede Komponente einer graphischen Oberfläche eine derartige Ereignisquelle sein. ◇ Zuständig für die Reaktion auf das Ereignis sind spezielle Ereignisempfänger, die sogenannten Event-Listener. Dies sind Objekte, die ein zum jeweiligen Ereignis passendes Bearbeitungs-Interface implementieren. Damit ein Event-Listener-Objekt die von einer bestimmten Ereignisquelle verursachten Ereignisse empfangen kann, muß es bei dieser Ereignisquelle als Listener registriert sein (Design Pattern Observer !). Dabei werden einem Listener nur die Ereignisse mitgeteilt, die zu seinem Typ passen, d.h. für die er ein passendes Bearbeitungs-Interface implementiert hat. ◇ Das Zustellen der einzelnen Ereignisse an die jeweils registrierten Event-Listener erfolgt durch den Aufruf der für den Ereignis-Typ zuständigen Bearbeitungsfunktion des Listeners (Call Back !). Dieser wird das Ereignis-Objekt als Parameter übergeben. In jedem GUI-Programm ist hierfür ein eigener Thread zuständig, der event-dispatching thread. ◇ Zwischen einer Ereignisquelle und einem Event-Listener muß keine 1:1-Beziehung bestehen. Bei einer Ereignisquelle können durchaus mehrere Event-Listener registriert sein – sogar für den gleichen Ereignis-Typ. Das Ereignis wird dann allen bei der Quelle für den jeweiligen Ereignis-Typ registrierten Event-Listenern zugestellt. Andererseits kann ein Event-Listener auch gleichzeitig bei mehreren Ereignisquellen registriert sein. Ereignisquelle 1 event Ereignisquelle 2 event Event-Listener 1 Event-Listener 2 Event-Listener 3 event Ereignisquelle 3 event Event-Listener 4 ◇ Das in Java seit dem JDK 1.1 implementierte Delegation Event Model wird sowohl für AWT-Komponenten als auch Swing-Komponenten angewendet. ◇ Es ermöglicht eine klare Trennung zwischen dem Programmcode zur Oberflächengestaltung (GUI-Klassen !) und dem Code zur Ereignisverarbeitung (Event-Listener-Klassen) als Schnittstelle zur Anwendungslogik. 49 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 572 – 00 – TH – 03 ------------------------------------------------------------------------------------ Ereignisverarbeitung in JFC-GUI-Komponenten (2) Ereignis-Klassen ◇ Auszug aus der Hierarchie der Ereignisklassen AWT AWTEvent (java.awt) Swing EventObject (java.util) ChangeEvent ListSelectionEvent CaretEvent MenuEvent (javax.swing.event) (javax.swing.event) (javax.swing.event) (javax.swing.event) . . . weitere EventKlassen ComponentEvent (java.awt.event) ActionEvent (java.awt.event) InputEvent (java.awt.event) ContainerEvent (java.awt.event) KeyEvent (java.awt.event) ItemEvent (java.awt.event) WindowEvent (java.awt.event) AncestorEvent (javax.swing.event) FocusEvent (java.awt.event) . . . weitere EventKlassen . . . weitere EventKlassen MouseEvent (java.awt.event) ◇ Die verschiedenen Event-Klassen implementieren jeweils Memberfunktionen mit denen Informationen, die mit dem jeweiligen Event-Objekt verknüpft sind, ermittelt werden können. ◇ Die Klasse EventObject, Basisklasse aller Ereignis-Klassen, stellt die folgende Methode zur Verfügung : public Object getSource() Ermittlung der ursprünglichen Ereignisquelle Diese Methode wird von allen anderen Ereignis-Klassen geerbt und kann damit für alle Event-Objekte aufgerufen werden. ◇ Die verschiedenen Ereignis-Klassen sind jeweils bestimmten Ereignisse auslösenden Aktionen zugeordnet. Beispielsweise wird ein ActionEvent-Objekt durch folgende Aktionen erzeugt : - Betätigung eines Schaltknopfes - Selektierung eines Auswahlfeldes - Auswahl eines Menue- oder Auswahllisten-Eintrags - Betätigung der RET-Taste ◇ In vielen Fällen kann bei einer Komponente einer bestimmten GUI-Klasse ein Objekt einer bestimmten Ereignis-Klasse nur durch eine einzige Aktionsart erzeugt werden. Diese Aktionsart kann bei den verschiedenen GUI-Klassen durchaus unterschiedlich sein. Es gibt aber auch Ereignis-Klassen, die für mehrere verschiedene Aktionsarten bei der gleichen Komponente zuständig sind. Beispielsweise ist die Klasse MouseEvent sowohl für Mausbewegungen (mouse motion events) als auch für alle sonstigen Mausereignisse (mouse events, wie Knopfbetätigung, Maus-Eintritt, Maus-Austritt in Komponente) zuständig 50 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 573 – 00 – TH – 02 ----------------------------------------------------------------------------------- Ereignisverarbeitung in JFC-GUI-Komponenten (3) Ereignis-Klassen, Forts. ◇ Da auf den verschiedenen GUI-Komponenten jeweils nur spezifische Aktionen ausgeführt werden können, können diese auch nur Ereignisse ganz bestimmter Klassen auslösen. Einige Ereignis-Klassen werden bereits in den Basisklassen der JFC-Hierarchie unterstützt. Entsprechende Ereignisse können daher in allen Komponenten der davon abgeleiteten Klassen ausgelöst werden. Einige GUI-Komponenten und die von ihnen unterstützen Ereignis-Klassen : GUI-Klasse unterstützte Ereignis-Klasse auslösende Aktionn Component ComponentEvent FocusEvent KeyEvent MouseEvent Position, Größe oder Sichtbarkeit wurden geändert Focus wurde erhalten oder verloren Tastatur wurde betätigt Maus wurde betätigt oder bewegt Container ContainerEvent Container-Inhalt wurde verändert Window WindowEvent Status des Fensters hat sich geändert JComponent AncestorEvent Umgebender Container hat sich verändert JButton ActionEvent ChangeEvent Schaltknopf wurde betätigt Zustand des Schaltknopfes hat sich geändert JTextField ActionEvent CaretEvent RET-Taste wurde betätigt Cursorposition hat sich geändert JTextArea CaretEvent Cursorposition hat sich geändert JCheckBox und JRadioButton ActionEvent ChangeEvent ItemEvent Auswahlfeld wurde betätigt Zustand des Auswahlfelds hat sich geändert Selektionszustand hat sich geändert JList ListSelectionEvent … Selektion wurde geändert ◇ Memberfunktionen (Auswahl) einiger Ereignis-Klassen zur Informationsermittlung über das Ereignis : ▻ Klasse AWTEvent public int getID() Ermittlung der auslösenden Aktionsart (Klassenkonstante !) ▻ Klasse ActionEvent public String getActionCommand() Ermittlung der für das Ereignis festgelegten Aktionskennung ▻ Klasse ItemEvent public Object getItem() Ermittlung des vom Ereignis betroffenen Objekts public int getStateChange() Ermittlung des geänderten Zustands (selektiert oder nicht selektiert) ▻ Klasse WindowEvent public Window getWindow() Ermittlung des Fensters, das das Ereignis ausgelöst hat 51 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 574 – 00 – TH – 03 ----------------------------------------------------------------------------------- Ereignisverarbeitung in JFC-GUI-Komponenten (4) Listener-Interfaces ◇ Die beim Auftritt eines Ereignisses informierten Event-Listener müssen jeweils ereignisspezifisch reagieren können. Ein Ereignis einer bestimmten Klasse kann nur ein Listener empfangen, der bestimmte zur Ereignis-Klasse passende Reaktions-Methoden bereitstellt. Jeder Listener muss ein bestimmtes Interface implementieren. ◇ Zu jeder Ereignis-Klasse ist mindestens ein passendes Listener-Interface definiert. Für einige Ereignis-Klassen gibt es auch mehrere Listener-Interfaces. Allgemein gilt : Zur Ereignis-Klasse AbcEvent existiert das Listener-Interface AbcListener. Beispiele : Ereignisklasse ActionEvent Listener-Interface ActionListener Ereignisklasse ItemEvent Listener-Interface ItemListener Bei mehreren mit einer Ereignis-Klasse korrespondierenden Listener-Interfaces gilt : Beispiel : Zur Ereignis-Klasse AbcEvent existieren Listener-Interfaces AbcXyzListener Ereignis-Klasse WindowEvent Listener-Interface WindowListener WindowFocusListener WindowStateListener ◇ Die verschiedenen Interfaces sind definiert in den Packages java.awt.event und javax.swing.event. Hierarchie der EventListener-Interfaces ◇ Alle Listener-Interfaces sind vom Interface EventListener (Package java.util) abgeleitet. Dieses deklariert keine Methoden sondern ist ein reines Marker-Interface. ◇ Einige Interfaces deklarieren genau eine Methode. Sie sind i.a. sogenannten semantischen Ereignissen zugeordnet. Andere Interfaces, die i.a. sogenannten Low-Level-Ereignissen zugeordnet sind, deklarieren mehrere Methoden. Eine Event-Listener-Klasse muß alle Methoden des Interfaces, das sie implementiert, definieren. Die jeweilige Methode enthält den vom Programm als Reaktion auf das Ereignis auszuführenden Code ( Event-Handler) Die Information eines Event-Listeners über ein eingetretenes Ereignis erfolgt durch den Aufruf einer dieser Methoden als Callback. ◇ Alle Methoden besitzen genau einen Parameter von der jeweiligen zugehörigen Ereignis-Klasse. 52 Beim Aufruf einer Methode wird ihr das ausgelöste Ereignis-Objekt als aktueller Parameter übergeben. ◇ Einige Listener-Interfaces als Beispiele : ▻ Interface ActionListener (Package java.awt.event) public void actionPerformed(ActionEvent e) aufgerufen, wenn ein ActionEvent auftritt ▻ Interface ItemListener (Package java.awt.event) public void itemStateChanged(ItemEvent e) aufgerufen, wenn ein ItemEvent auftritt ▻ Interface WindowListener (Package java.awt.event), unvollständig public void windowClosing(WindowEvent e) aufgerufen, wenn Fenster geschlossen werden soll public void windowClosed(WindowEvent e) aufgerufen, wenn Fenster geschlossen wurde public void windowActivated(WindowEvent e) aufgerufen, wenn Fenster aktiviert wurde public void windowDeactivated(WindowEvent e) aufgerufen, wenn Fenster deaktiviert wurde public void windowIconified(WindowEvent e) aufgerufen, wenn Fenster minimiert wurde 53 Key-Events Unter Windows werden alle Tastatureingaben an die fokussierte Komponente gesendet. Ein Empfänger für Key-Events muß das Interface KeyListener implementieren und bekommt Events des Typs KeyEvent übergeben. KeyEvent erweitert die Klasse InputEvent, die ihrerseits aus ComponentEvent abgeleitet ist, und stellt neben getID und getSource eine ganze Reihe von Methoden zur Verfügung, mit denen die Erkennung und Bearbeitung der Tastencodes vereinfacht wird. Die Registrierung von Key-Events erfolgt mit der Methode addKeyListener, die auf allen Objekten des Typs Component oder daraus abgeleiteten Klassen zur Verfügung steht: public void addKeyListener(KeyListener l) java.awt.Component Das Interface KeyListener public abstract void public abstract void public abstract void definiert drei unterschiedliche Methoden: keyPressed(KeyEvent e) keyReleased(KeyEvent e) keyTyped(KeyEvent e) Mouse-Events l Ein Mouse-Event entsteht, wenn der Anwender innerhalb der Client-Area des Fensters eine der Maustasten drückt oder losläßt. Dabei reagiert das Programm sowohl auf Klicks der linken als auch - falls vorhanden - der rechten Maustaste und zeigt an, welche der Umschalttasten [STRG], [ALT], [UMSCHALT] oder [META] während des Mausklicks gedrückt waren. Des weiteren ist es möglich, zwischen einfachen und doppelten Mausklicks zu unterscheiden. Ein Empfänger für Mouse-Events muß das Interface MouseListener implementieren und bekommt Events des Typs MouseEvent übergeben. MouseEvent erweitert die Klasse InputEvent und stellt neben getID und getSource eine Reihe zusätzlicher Methoden zur Verfügung, die wichtige Informationen liefern. Die Registrierung der Empfängerklasse erfolgt mit der Methode addMouseListener, die in allen Klassen zur Verfügung steht, die aus Component abgeleitet wurden: public void addMouseListener(MouseListener l) Tabelle 29.3 gibt eine Übersicht der Methoden von MouseListener und erklärt ihre Bedeutung: Ereignismethode Bedeutung mousePressed Eine Maustaste wurde gedrückt. mouseReleased Die gedrückte Maustaste wurde losgelassen. mouseClicked Eine Maustaste wurde gedrückt und wieder losgelassen. Diese Methode wird nach mouseReleased aufgerufen. mouseEntered Der Mauszeiger wurde in den Client-Bereich der auslösenden Komponente hineinbewegt. mouseExited Der Mauszeiger wurde aus dem Client-Bereich der auslösenden Komponente herausbewegt. 54 FACHHOCHSCHULE MUENCHEN FAKULTÄT ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 575 – 00 – TH – 04 ----------------------------------------------------------------------------------- Ereignisverarbeitung in JFC-GUI-Komponenten (5) Listener-Adapter-Klassen ◇ Eine Klasse, die ein Listener-Interface implementiert, dass mehrere Methoden deklariert, muss sämtliche Methoden definieren, auch wenn in der speziellen Anwendung nur eine von ihnen benötigt wird. ◇ Zur Vereinfachung der Definition von Listener-Klassen existieren für derartige Interfaces Adapter-Klassen. Es handelt sich bei Ihnen um abstrakte Klassen, die sämtliche Methoden des Interfaces mit leerer Funktionalität implementieren. Statt eine Listener-Klasse das jeweilige Listener-Interface direkt implementieren zu lassen, leitet man die Klasse von der zugehörigen Adapter-Klasse ab. Die Klasse muß dann lediglich die im konkreten Fall benötigte Methode überschreiben. ◇ Wenn zum Listener-Interface AbcListener eine Adapter-Klasse existiert, lautet ihr Name AbcAdapter. Beispiel : Listener-Interface WindowListener Listener-Adapter WindowAdapter Listener-Klassen ◇ Sie müssen das Listener-Interface, das zu der Ereignis-Klasse, deren Objekte sie empfangen sollen, gehört, implementieren. Eine Listener-Klasse kann auch mehrere Listener-Interfaces implementieren und damit auf Ereignisse unterschiedlicher Klassen reagieren. ◇ Eine Listener-Klasse kann das oder die Listener-Interfaces ▻ entweder direkt implementieren ▻ oder von einer zugehörigen Adapter-Klasse abgeleitet werden. ◇ Zur programmtechnischen Realisierung eine Listener-Klasse existieren folgende Möglichkeiten : (siehe Bspiele) ▻ Realisierung der Listener-Klasse als innere Klasse ▻ Realisierung der Listener-Klasse als lokale Klasse oder anonyme Klasse ▻ Realisierung der Listener-Klasse als Top-Level-Klasse ▻ Verwendung der Container-Klasse als Listener-Klasse Implementierung einer Ereignisverarbeitung im Anwender-Code ◆ Definition einer geeigneten Event-Listener-Klasse. In der (bzw den) zu implementierenden Interface-Methode(n) (Event-Handler) ist die Programmfunktionalität zu realisieren, die als Reaktion auf den Eintritt eines Ereignisses der betreffenden Art vorgesehen ist. ◆ Erzeugung eines Objekts dieser Event-Listener-Klasse (Event-Listener-Objekt). ◆ Registrierung des Event-Listener-Objekts bei der auf Ereignisse zu "überwachenden" GUI-Komponente. Hierfür stellen die GUI-Komponenten für die von ihnen unterstützten Listener-Typen entsprechende Registrierungsfunktionen zur Verfügung : Für EventListener vom Typ AbcListener Funktion void addAbcListener(AbcListener lis) Beispiel : Klasse JButton für ActionListener-Objekte (geerbt von der Klasse AbstractButton) : public void addActionListener(ActionListener lis) Registrierung des Action-Listener-Objects lis ◆ Gegebenenfalls Festlegung von spezifischen Informationen, die einem Ereignis-Objekt übergeben werden. Beispiel : Klasse JButton : Setzen einer Aktionskennung (action command) für ActionListener-Objekte (geerbt von der Klasse AbstractButton) : public void setActionCommand(String cmd) 55 Setzen der Aktionskennung cmd Beispiele Variante 1: Implementierung eines EventListener-Interfaces Bei der ersten Variante gibt es nur eine einzige Klasse, Listing2802. Sie ist einerseits eine Ableitung der Klasse JFrame, um ein Fenster auf dem Bildschirm darzustellen und zu beschriften. Andererseits implementiert sie das Interface KeyListener, das die Methoden keyPressed, keyReleased und keyTyped definiert. Der eigentliche Code zur Reaktion auf die Taste [ESC] steckt in der Methode keyPressed, die immer dann aufgerufen wird, wenn eine Taste gedrückt wurde. Mit der Methode getKeyCode der Klasse KeyEvent wird auf den Code der gedrückten Taste zugegriffen und dieser mit der symbolischen Konstante VK_ESCAPE verglichen. Stimmen beide überein, wurde [ESC] gedrückt, und das Programm kann beendet werden. 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 /* Listing2802.java */ import java.awt.*; import java.awt.event.*; public class Listing2802 extends JFrame implements KeyListener { public static void main(String[] args) { Listing2802 wnd = new Listing2802(); } public Listing2802() { super("Nachrichtentransfer"); setBackground(Color.lightGray); setSize(300,200); setLocation(200,100); setVisible(true); addKeyListener(this); } public void paint(Graphics g) { g.setFont(new Font("Serif",Font.PLAIN,18)); g.drawString("Zum Beenden bitte ESC drücken...",10,50); } public void keyPressed(KeyEvent event) { if (event.getKeyCode() == KeyEvent.VK_ESCAPE) { setVisible(false); dispose(); System.exit(0); } } public void keyReleased(KeyEvent event) { } public void keyTyped(KeyEvent event) { } } Listing2802.java 56 Listing 28.2: Implementieren eines Listener-Interfaces Die Verbindung zwischen der Ereignisquelle (in diesem Fall der Fensterklasse Listing2802) und dem Ereignisempfänger (ebenfalls die Klasse Listing2802) erfolgt über den Aufruf der Methode addKeyListener der Klasse JFrame. Alle Tastaturereignisse werden dadurch an die Fensterklasse selbst weitergeleitet und führen zum Aufruf der Methoden keyPressed, keyReleased oder keyTyped des Interfaces KeyListener. Diese Implementierung ist sehr naheliegend, denn sie ist einfach zu implementieren und erfordert keine weiteren Klassen. Nachteilig ist dabei allerdings: lEs besteht keine Trennung zwischen GUI-Code und Applikationslogik. Dies kann große Programme unübersichtlich und schwer wartbar machen. l Für jeden Ereignistyp muss eine passende Listener-Klasse registriert werden. Da viele der EventListenerInterfaces mehr als eine Methode definieren, werden dadurch schnell viele leere Methodenrümpfe in der Fensterklasse zu finden sein. In diesem Beispiel sind es schon keyReleased und keyTyped, bei zusätzlichen Interfaces würden schnell weitere hinzukommen. Es bleibt festzuhalten, dass diese Technik bestenfalls für kleine Programme geeignet ist, die nur begrenzt erweitert werden müssen. Durch die Vielzahl leerer Methodenrümpfe können aber auch kleine Programme schnell unübersichtlich werden. Variante 2: Lokale und anonyme Klassen Die zweite Alternative bietet eine bessere Lösung. Sie basiert auf der Verwendung lokaler bzw. anonymer Klassen und kommt ohne die Nachteile der vorigen Version aus. Sie ist das in der Dokumentation des JDK empfohlene Entwurfsmuster für das Event-Handling in kleinen Programmen oder bei Komponenten mit einfacher Nachrichtenstruktur. Vor ihrem Einsatz sollte man allerdings das Prinzip lokaler und anonymer Klassen kennenlernen, das mit dem JDK 1.1 in Java eingeführt und in Abschnitt 10.1 vorgestellt wurde. Wer diesen Abschnitt noch nicht gelesen hat, sollte das jetzt nachholen. Lokale Klassen Die Anwendung lokaler Klassen für die Ereignisbehandlung besteht darin, mit ihrer Hilfe die benötigten EventListener zu implementieren. Dazu wird in dem GUI-Objekt, das einen Event-Handler benötigt, eine lokale Klasse definiert und aus einer passenden Adapterklasse abgeleitet. Nun braucht nicht mehr das gesamte Interface implementiert zu werden (denn die Methodenrümpfe werden ja aus der Adapterklasse geerbt), sondern lediglich die tatsächlich benötigten Methoden. Da die lokale Klasse zudem auf die Membervariablen und Methoden der Klasse zugreifen kann, in der sie definiert wurde, lassen sich auf diese Weise sehr schnell die benötigten Ereignisempfänger zusammenbauen. Das folgende Beispiel definiert eine lokale Klasse MyKeyListener, die aus KeyAdapter abgeleitet wurde und auf diese Weise das KeyListener-Interface implementiert. Sie überlagert lediglich die Methode keyPressed, um auf das Drücken einer Taste zu reagieren. Als lokale Klasse hat sie außerdem Zugriff auf die Methoden der umgebenden Klasse und kann somit durch Aufruf von setVisible und dispose das Fenster, in dem sie als Ereignisempfänger registriert wurde, schließen. Die Registrierung der lokalen Klasse erfolgt durch Aufruf von addKeyListener, bei dem gleichzeitig eine Instanz der lokalen Klasse erzeugt wird. Als lokale Klasse ist MyKeyListener überall innerhalb von Listing2803 sichtbar und kann an beliebiger Stelle instanziert werden. 001 /* Listing2803.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; import javax.swing.*; 005 006 public class Listing2803 007 extends JFrame 008 { 009 public static void main(String[] args) 010 { 011 Listing2803 wnd = new Listing2803(); 012 } 013 014 public Listing2803() 015 { 016 super("Nachrichtentransfer"); 017 setBackground(Color.lightGray); 018 setSize(300,200); 019 setLocation(200,100); 020 setVisible(true); 021 addKeyListener(new MyKeyListener()); 022 } 023 57 024 025 026 027 028 public void paint(Graphics g) { g.setFont(new Font("Serif",Font.PLAIN,18)); g.drawString("Zum Beenden bitte ESC drücken...",10,50); } 029 030 class MyKeyListener 031 extends KeyAdapter 032 { 033 public void keyPressed(KeyEvent event) 034 { 035 if (event.getKeyCode() == KeyEvent.VK_ESCAPE) { 036 setVisible(false); 037 dispose(); 038 System.exit(0); 039 } 040 } 041 } 042 } Listing 28.3: Verwendung lokaler Klassen Der Vorteil dieser Vorgehensweise ist offensichtlich: es werden keine unnützen Methodenrümpfe erzeugt, aber trotzdem verbleibt der Ereignisempfängercode wie im vorigen Beispiel innerhalb der Ereignisquelle. Dieses Verfahren ist also immer dann gut geeignet, wenn es von der Architektur oder der Komplexität der Ereignisbehandlung her sinnvoll ist, Quelle und Empfänger zusammenzufassen. Anonyme Klassen Das folgende Beispiel ist eine leichte Variation des vorigen. Es zeigt die Verwendung einer anonymen Klasse, die aus KeyAdapter abgeleitet wurde, als Ereignisempfänger. Zum Instanzierungszeitpunkt erfolgt die Definition der überlagernden Methode keyPressed, in der der Code zur Reaktion auf das Drücken der Taste [ESC] untergebracht wird. 001 /* Listing2804.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; import javax.swing.* 005 006 public class Listing2804 007 extends JFrame 008 { 009 public static void main(String[] args) 010 { 011 Listing2804 wnd = new Listing2804(); 012 } 013 014 public Listing2804() 015 { 016 super("Nachrichtentransfer"); 017 etBackground(Color.lightGray); 018 setSize(300,200); 019 setLocation(200,100); 020 setVisible(true); 021 addKeyListener( 022 new KeyAdapter() { 023 public void keyPressed(KeyEvent event) 024 { 025 if (event.getKeyCode() == KeyEvent.VK_ESCAPE) { 026 setVisible(false); 027 dispose(); 028 System.exit(0); 029 } 030 } 031 } 032 ); 033 } 034 58 035 public void paint(Graphics g) 036 { 037 g.setFont(new Font("Serif",Font.PLAIN,18)); 038 g.drawString("Zum Beenden bitte ESC drücken...",10,50); 039 } 040 } Listing 28.4: Verwendung einer anonymen Klasse als Ereignishandler Vorteilhaft bei dieser Vorgehensweise ist der verminderte Aufwand, denn es muss keine separate Klassendefinition angelegt werden. Statt dessen werden die wenigen Codezeilen, die zur Anpassung der Adapterklasse erforderlich sind, dort eingefügt, wo die Klasse instanziert wird, nämlich beim Registrieren des Nachrichtenempfängers. Anonyme Klassen haben einen ähnlichen Einsatzbereich wie lokale, empfehlen sich aber vor allem, wenn sehr wenig Code für den Ereignisempfänger benötigt wird. Bei aufwendigeren Ereignisempfängern ist die explizite Definition einer benannten Klasse dagegen vorzuziehen. Variante 3: Trennung von GUI- und Anwendungscode Wir hatten am Anfang darauf hingewiesen, daß in größeren Programmen eine Trennung zwischen Programmcode, der für die Oberfläche zuständig ist, und solchem, der für die Anwendungslogik zuständig ist, wünschenswert wäre. Dadurch wird eine bessere Modularisierung des Programms erreicht, und der Austausch oder die Erweiterung von Teilen des Programms wird erleichtert. Das Delegation Event Model wurde auch mit dem Designziel entworfen, eine solche Trennung zu ermöglichen bzw. zu erleichtern. Der Grundgedanke dabei war es, auch Nicht-Komponenten die Reaktion auf GUI-Events zu ermöglichen. Dies wurde dadurch erreicht, daß jede Art von Objekt als Ereignisempfänger registriert werden kann, solange es die erforderlichen Listener-Interfaces implementiert. Damit ist es möglich, die Anwendungslogik vollkommen von der grafischen Oberfläche abzulösen und in Klassen zu verlagern, die eigens für diesen Zweck entworfen wurden. Hinweis Das nachfolgende Beispiel zeigt diese Vorgehensweise, indem es unser Beispielprogramm in die drei Klassen Listing2805, MainFrameCommand und MainFrameGUI aufteilt. Listing2805 enthält nur noch die mainMethode und dient lediglich dazu, die anderen beiden Klassen zu instanzieren. MainFrameGUI realisiert die GUIFunktionalität und stellt das Fenster auf dem Bildschirm dar. MainFrameCommand spielt die Rolle des Kommandointerpreters, der immer dann aufgerufen wird, wenn im Fenster ein Tastaturereignis aufgetreten ist. Die Verbindung zwischen beiden Klassen erfolgt durch Aufruf der Methode addKeyListener in MainFrameGUI, an die das an den Konstruktor übergebene MainFrameCommand-Objekt weitergereicht wird. Dazu ist es erforderlich, daß das Hauptprogramm den Ereignisempfänger cmd zuerst instanziert, um ihn bei der Instanzierung des GUI-Objekts gui übergeben zu können. Umgekehrt benötigt natürlich auch das Kommando-Objekt Kenntnis über das GUI-Objekt, denn es soll ja das zugeordnete Fenster schließen und das Programm beenden. Der scheinbare Instanzierungskonflikt durch diese zirkuläre Beziehung ist aber in Wirklichkeit gar keiner, denn bei jedem Aufruf einer der Methoden von MainFrameCommand wird an das KeyEvent-Objekt der Auslöser der Nachricht übergeben, und das ist in diesem Fall stets das MainFrameGUI-Objekt gui. So kann innerhalb des Kommando-Objekts auf alle öffentlichen Methoden des GUIObjekts zugegriffen werden. Tip 001 /* Listing2805.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 import javax.swing.* 006 public class Listing2805 007 { 008 public static void main(String[] args) 009 { 010 MainFrameCommand cmd = new MainFrameCommand(); 011 MainFrameGUI gui = new MainFrameGUI(cmd); 012 } 013 } 014 59 015 class MainFrameGUI 016 extends JFrame 017 { 018 public MainFrameGUI(KeyListener cmd) 019 { 020 super("Nachrichtentransfer"); 021 setBackground(Color.lightGray); 022 setSize(300,200); 023 setLocation(200,100); 024 setVisible(true); 025 addKeyListener(cmd); 026 } 027 028 public void paint(Graphics g) 029 { 030 g.setFont(new Font("Serif",Font.PLAIN,18)); 031 g.drawString("Zum Beenden bitte ESC drücken...",10,50); 032 } 033 } 034 035 class MainFrameCommand 036 implements KeyListener 037 { 038 public void keyPressed(KeyEvent event) 039 { 040 JFrame source = (JFrame)event.getSource(); 041 if (event.getKeyCode() == KeyEvent.VK_ESCAPE) { 042 source.setVisible(false); 043 source.dispose(); 044 System.exit(0); 045 } 046 } 047 048 public void keyReleased(KeyEvent event) 049 { 050 } 051 052 public void keyTyped(KeyEvent event) 053 { 054 } 055 } Listing 28.5: Trennung von GUI- und Anwendungslogik Diese Designvariante ist vorwiegend für größere Programme geeignet, bei denen eine Trennung von Programmlogik und Oberfläche sinnvoll ist. Für sehr kleine Programme oder solche, die wenig Ereigniscode haben, sollte eher eine der vorherigen Varianten angewendet werden, wenn diese zu aufwendig ist. Sie entspricht in groben Zügen dem Mediator-Pattern, das in "Design-Patterns" von Gamma et al. beschrieben wird. Natürlich erhebt das vorliegende Beispielprogramm nicht den Anspruch, unverändert in ein sehr großes Programm übernommen zu werden. Es soll lediglich die Möglichkeit der Trennung von Programmlogik und Oberfläche in einem großen Programm mit Hilfe der durch das Event-Handling des JDK 1.1 vorgegebenen Möglichkeiten aufzeigen. Eine sinnvolle Erweiterung dieses Konzepts könnte darin bestehen, weitere Modularisierungen vorzunehmen (z.B. analog dem MVC-Konzept von Smalltalk, bei dem GUI-Anwendungen in Model-, View- und Controller-Layer aufgesplittet werden, oder auch durch Abtrennen spezialisierter Kommandoklassen). Empfehlenswert ist in diesem Zusammenhang die Lektüre der JDKDokumentation, die ein ähnliches Beispiel in leicht veränderter Form enthält. 60 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 576 – 00 – TH – 02 ----------------------------------------------------------------------------------- Ereignisverarbeitung in JFC-GUI-Komponenten (6) Demonstrationsprogramm zum Interface ActionListener // ColorChangeDemo.java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ColorChangeDemo extends JFrame { Container c; JButton but; public ColorChangeDemo() { super("ColorChangeDemo"); c=getContentPane(); c.setLayout(new FlowLayout()); but = new JButton("Change Backgroundcolor"); c.add(but); // ActionListener-Klasse als anonyme Klasse ActionListener actlis = new ActionListener() { public void actionPerformed(ActionEvent e) { float rwert = (float)Math.random(); float gwert = (float)Math.random(); float bwert = (float)Math.random(); c.setBackground(new Color(rwert, gwert, bwert)); } }; but.addActionListener(actlis); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { new ColorChangeDemo().setVisible(true); } } 61 FACHHOCHSCHULE MUENCHEN FACHBEREICH ELEKTROTECHNIK UND INFORMATIONSTECHNIK BEREICH DATENTECHNIK V – JV – 577 – 00 – TH – 01 ----------------------------------------------------------------------------------- Ereignisverarbeitung in JFC-GUI-Komponenten (6) Demonstrationsprogramm zur Adapter-Klasse WindowAdapter // WindowClosingDemo.java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class WindowClosingDemo extends JFrame { Container c; JLabel lab; JCheckBox cb1, cb2; public WindowClosingDemo() { super("Window Closing Demo"); c = getContentPane(); c.setLayout(new FlowLayout()); lab = new JLabel("Zum Schliessen des Fensters bitte " + "beide Auswahlfelder selektieren"); lab.setBorder(BorderFactory.createEmptyBorder(10, 0, 25, 0)); cb1 = new JCheckBox("Auswahl 1"); cb2 = new JCheckBox("Auswahl 2"); c.add(lab); c.add(cb1); c.add(cb2); addWindowListener(new ClosingListener()); setSize(400, 150); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); } // WindowListener-Klasse als innere Klasse public class ClosingListener extends WindowAdapter { public void windowClosing(WindowEvent e) { if (cb1.isSelected() && cb2.isSelected()) { e.getWindow().dispose(); System.exit(0); } else JOptionPane.showMessageDialog(c, "Vor dem Schliessen " + "beide Auswahlfelder selektieren"); } } public static void main(String[] args) { new WindowClosingDemo().setVisible(true); } } 62 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56C – 03 – TH – 01 ------------------------------------------------------------------------------------ Komplexe Swing-Komponenten-Klassen (1) JList Die Klasse JList dient dazu, Listen von Werten darzustellen, aus denen der Anwender einen oder mehrere Einträge auswählen kann. Im Gegensatz zur AWT-Klasse List kann sie nicht nur Strings, sondern beliebige Objekte enthalten. Auch die Darstellung der Listenelemente auf dem Bildschirm kann weitgehend frei gestaltet werden. Wir wollen uns die wichtigsten Eigenschaften von JList ansehen und dazu zunächst mit den Konstruktoren beginnen: public public public public JList() JList(Object[] listData) JList(Vector listData) JList(ListModel dataModel) Der parameterlose Konstruktor erzeugt eine leere Liste. Wird ein Array oder Vector übergeben, erzeugt JList aus dessen Daten ein Listenmodell und benutzt es zur Darstellung. Schließlich kann auch direkt eine Instanz der Klasse ListModel übergeben werden, um den Inhalt der Liste zu definieren. Ähnlich wie JTextArea und andere Swing-Komponenten besitzt JList keine eigene Funktionalität zum Scrollen der Daten, falls diese nicht vollständig auf den Bildschirm passen. Eine JList wird daher meist in eine JScrollPane eingebettet, bevor sie zu einem GUI-Container hinzugefügt wird. Hinweis Selektieren von Elementen Die meisten Methoden der Klasse JList haben mit der Selektion der Listenelemente zu tun. Eine Liste kann sowohl Einzel als auch Mehrfachselektion unterstützen: public int getSelectionMode() public void setSelectionMode(int selectionMode) Mit setSelectionMode wird der Selektionsmodus verändert. Als Argument kann eine der folgenden Konstanten der Klasse ListSelectionModel übergeben werden: l SINGLE_SELECTION: Es kann maximal ein Element ausgewählt werden l SINGLE_INTERVAL_SELECTION: Es können mehrere Elemente eines zusammenhängenden Bereichs ausgewählt werden l MULTIPLE_INTERVAL_SELECTION: Eine beliebige Auswahl von Elementen kann selektiert werden getSelectionMode liefert den aktuellen Selektionsmodus. Es gibt eine Reihe von Methoden, um Informationen über die derzeit selektierten Elemente zu beschaffen: public int getSelectedIndex() public int[] getSelectedIndices() public Object getSelectedValue() public Object[] getSelectedValues() public boolean isSelectedIndex(int index) public boolean isSelectionEmpty() public int getAnchorSelectionIndex() public int getLeadSelectionIndex() getSelectedIndex liefert den Index des selektierten Elements, falls der Selektionsmodus SINGLE_SELECTION ist. Können mehrere Elemente ausgewählt werden, liefert getSelectedIndices ein Array mit den Indizes aller selektierten Elemente. getSelectedValue und getSelectedValues arbeiten in analoger Weise, liefern aber statt der Indizes die selektierten Elemente zurück. Mit isSelectedIndex kann geprüft werden, ob das Element mit dem angegebenen Index gerade selektiert ist, und isSelectionEmpty prüft, ob mindestens ein Element selektiert wurde. Als "Anchor" und "Lead" bezeichnet Swing in einem zusammenhängend markierten Bereich das jeweils zuerst und zuletzt markierte Element. Das zuletzt markierte Element ist gleichzeitig aktuelles Element. Mit getAnchorSelectionIndex und getLeadSelectionIndex kann auf "Anchor" und "Lead" zugegriffen werden, wenn der Selektionsmodus SINGLE_INTERVAL_SELECTION ist. Zusätzlich gibt es Methoden, um die Selektion programmgesteuert zu verändern: public void clearSelection() public void setSelectedIndex(int index) public void setSelectedIndices(int[] indices) public void setSelectionInterval(int anchor, int lead) public void addSelectionInterval(int anchor, int lead) public void removeSelectionInterval(int index0, int index1) 63 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56C – 03 – TH – 01 ------------------------------------------------------------------------------------ Komplexe Swing-Komponenten-Klassen (2) Mit clearSelection wird die Selektion gelöscht. Mit setSelectedIndex kann ein einzelnes Element selektiert werden, mit setSelectedIndices eine Menge von Elementen. Mit setSelectionInterval, addSelectionInterval und removeSelectionInterval können Selektionen auch bereichsweise hinzugefügt und gelöscht werden. Wird die Selektion geändert, versendet eine JList ein ListSelectionEvent an alle registrierten Listener. Um im Programm auf Änderungen zu reagieren, ist also lediglich das Interface ListSelectionListener des Pakets javax.swing.event zu implementieren und durch Aufruf von addListSelectionListener bei der JList zu registrieren. Jede Selektionsänderung führt dann zum Aufruf der Methode valueChanged. Den Listeninhalt dynamisch verändern Etwas mehr Aufwand als beim AWT-Pendant muß getrieben werden, wenn der Inhalt einer JList nach der Instanzierung modifiziert werden soll. In diesem Fall kann nicht mehr mit dem automatisch erzeugten Listenmodel gearbeitet werden, sondern es muß selbst eines erzeugt werden. Das Modell einer JList muß stets das Interface ListModel implementieren und der Liste durch Versenden eines ListDataEvent jede Datenänderung mitteilen. Eine für viele Zwecke ausreichende Implementierung steht mit der Klasse DefaultListModel zur Verfügung. Ihre Schnittstelle entspricht der Klasse Vector und alle erforderlichen Änderungsbenachrichtigungen werden automatisch verschickt. Die wichtigsten Methoden von DefaultListModel sind: public void clear() public void addElement(Object obj) public void removeElementAt(int index) public int size() public Object elementAt(int index) Soll eine Liste mit einem benutzerdefinierten Modell arbeiten, wird dieses einfach manuell erzeugt und an den Konstruktor übergeben. Alle Einfügungen, Löschungen und Änderungen von Daten werden dann an diesem Modell vorgenommen. Durch Aufruf von getModel kann auf einfache Weise auf das Modell einer JList zugegriffen werden. Beispiel Zum Abschluss wollen wir uns ein Beispiel ansehen. Das folgende Programm instanziert eine JList durch Übergabe eines String-Arrays. Bei jedem Drücken des Buttons "Ausgabe" gibt es die Liste der selektierten Elemente auf der Konsole aus: 001 /* Listing3710.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 import javax.swing.*; 006 007 public class Listing3710 008 extends JFrame 009 implements ActionListener 010 { 011 static final String[] DATA = { 012 "Hund", "Katze", "Meerschweinchen", "Tiger", "Maus", 013 "Fisch", "Leopard", "Schimpanse", "Kuh", "Pferd", 014 "Reh", "Huhn", "Marder", "Adler", "Nilpferd" 015 }; 016 017 private JList list; 018 019 public Listing3710() 020 { 021 super("JList"); 022 023 Container cp = getContentPane(); 024 //Liste 025 list = new JList(DATA); 026 list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 029 list.setSelectedIndex(2); 64 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – JV – 56C – 03 – TH – 01 ------------------------------------------------------------------------------------ Komplexe Swing-Komponenten-Klassen (3) 030 cp.add(new JScrollPane(list), BorderLayout.CENTER); 031 //Ausgabe-Button 032 JButton button = new JButton("Ausgabe"); 033 button.addActionListener(this); 034 cp.add(button, BorderLayout.SOUTH); 035 } 036 037 public void actionPerformed(ActionEvent event) 038 { 039 String cmd = event.getActionCommand(); 040 if (cmd.equals("Ausgabe")) { 041 System.out.println("---"); 042 ListModel lm = list.getModel(); 043 int[] sel = list.getSelectedIndices(); 044 for (int i = 0; i < sel.length; ++i) { 045 String value = (String)lm.getElementAt(sel[i]); 046 System.out.println(" " + value); 047 } 048 } 049 } 050 051 public static void main(String[] args) 052 { 053 Listing3710 frame = new Listing3710(); 054 frame.setLocation(100, 100); 055 frame.setSize(200, 200); 056 frame.setVisible(true); 057 } 058 } Listing 37.10: Die Klasse JList Die Programmausgabe ist: 65 Listing3710 mit DefaultListModel //Das interne ListModel verfügt nur über die Methoden getSize u. getElementAt //Falls man z. auch Einfügen und Löschen will, muss das DefaultListModel oder //ein eigenes ListModel verwendet werden package list; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Listing3710 extends JFrame implements ActionListener { //Membervariable static final String[] DATA = { "Hund", "Katze", "Meerschweinchen", "Tiger", "Maus", "Fisch", "Leopard", "Schimpanse", "Kuh", "Pferd", "Reh", "Huhn", "Marder", "Adler", "Nilpferd" }; private JList list=null; //Default-ListModel, Schnittstelle entspricht der Klasse Vector private DefaultListModel lm= new DefaultListModel(); private JTextField jTextField1 = new JTextField(); public Listing3710() { super("JList-Demo DefaultListModel"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); //Liste list = new JList(lm/*DATA*/); list.setModel(lm); for(int i=0;i<DATA.length;i++) { lm.addElement(DATA[i]); } list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setSelectedIndex(2); cp.add(new JScrollPane(list), BorderLayout.CENTER); //Ausgabe-Button JButton button = new JButton("Ausgabe+Delete"); button.addActionListener(this); cp.add(button, BorderLayout.SOUTH); } public void actionPerformed(ActionEvent event) { String cmd = event.getActionCommand(); if (cmd.equals("Ausgabe+Delete")) { System.out.println("---"); //ListModel lm = list.getModel(); int[] sel = list.getSelectedIndices(); for (int i = 0; i < sel.length; ++i) { String value = (String)lm.getElementAt(sel[i]); System.out.println(" " + value); 66 lm.removeElementAt(sel[i]); //nur bei DefaultListModel } } } public static void main(String[] args) { Listing3710 frame = new Listing3710(); frame.setLocation(100, 100); frame.setSize(200, 200); frame.setVisible(true); } } 67 FACHHOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK ------------------------------------------------------------------------------------ Entwurfsmuster (design-pattern) mit GUI-Bezug Design-Patterns (oder Entwurfsmuster) sind eine der wichtigsten und interessantesten Entwicklungen der objektorientierten Programmierung der letzten Jahre. Basierend auf den Ideen des Architekten Christopher Alexander wurden sie durch das Buch "Design-Patterns - Elements of Reusable Object-Oriented Software" von Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides 1995 einer breiten Öffentlichkeit bekannt. Als Design-Patterns bezeichnet man (wohlüberlegte) Designvorschläge für den Entwurf objektorientierter Softwaresysteme. Ein Design-Pattern deckt dabei ein ganz bestimmtes Entwurfsproblem ab und beschreibt in rezeptartiger Weise das Zusammenwirken von Klassen, Objekten und Methoden. Meist sind daran mehrere Algorithmen und/oder Datenstrukturen beteiligt. Design-Patterns stellen wie Datenstrukturen oder Algorithmen vordefinierte Lösungen für konkrete Programmierprobleme dar, allerdings auf einer höheren Abstraktionsebene. Einer der wichtigsten Verdienste standardisierter Design-Patterns ist es, Softwaredesigns Namen zu geben. Zwar ist es in der Praxis nicht immer möglich oder sinnvoll, ein bestimmtes Design-Pattern in allen Details zu übernehmen. Die konsistente Verwendung ihrer Namen und ihres prinzipiellen Aufbaus erweitern jedoch das Handwerkszeug und die Kommunikationsfähigkeit des OOP-Programmierers beträchtlich. Begriffe wie Factory, Iterator oder Singleton werden in OOP -Projekten routinemäßig verwendet und sollten für jeden betroffenen Entwickler dieselbe Bedeutung haben. Wir wollen nachfolgend einige der wichtigsten Design-Patterns vorstellen und ihre Implementierung in Java skizzieren. Die Ausführungen sollten allerdings nur als erster Einstieg in das Thema angesehen werden. Viele Patterns können hier aus Platzgründen gar nicht erwähnt werden, obwohl sie in der Praxis einen hohen Stellenwert haben (z.B. Adapter, Bridge, Mediator, Command etc.). Zudem ist die Bedeutung eines Patterns für den OOP-Anfänger oft gar nicht verständlich, sondern erschließt sich erst nach Monaten oder Jahren zusätzlicher Programmiererfahrung. Die folgenden Abschnitte ersetzen also nicht die Lektüre weiterführender Literatur zu diesem Thema. Das oben erwähnte Werk von Gamma et al. ist nach wie vor einer der Klassiker schlechthin (die Autoren und ihr Buch werden meist als "GoF" bezeichnet, ein Akronym für "Gang of Four"). Daneben existieren auch spezifische Kataloge, in denen die Design-Patterns zu bestimmten Anwendungsgebieten oder auf der Basis einer ganz bestimmten Sprache, wie etwa C++ oder Java, beschrieben werden. 68 Composite-Muster In der Programmierpraxis werden häufig Datenstrukturen benötigt, bei denen die einzelnen Objekte zu Baumstrukturen zusammengesetzt werden können. Es gibt viele Beispiele für derartige Strukturen: l Die Menüs in einem Programm enthalten Menüpunkte und Untermenüs. Untermenüs enthalten weitere Menüpunkte, sind aber selbst Menüpunkte im übergeordneten Menü. l Ein Verzeichnis in einem Dateisystem enthält Dateien und Unterverzeichnisse. Unterverzeichnisse weisen dieselbe prinzipielle Struktur wie ihre übergeordneten Verzeichnisse auf. Sowohl Dateien als auch Unterverzeichnisse haben gemeinsame Eigenschaften, wie etwa einen Namen oder zugeordnete Rechte. l Die Komponenten einer grafischen Oberfläche können sowohl einfache Dialogelemente (Buttons, Textfelder, Listboxen) als auch Container (Unterfenster, Split-Panels, Tab-Panels) sein. Container enthalten ebenfalls Komponenten (vergleiche zum Beispiel die Hierarchie der AWT-Fensterklassen). l Ein mechanisches Bauteil besteht aus elementaren Teilen, die nicht mehr weiter zerlegt werden können, und zusammengesetzten Bauteilen, die aus Unterbauteilen bestehen. Für diese häufig anzutreffende Abstraktion gibt es ein Design-Pattern, das als Composite bezeichnet wird. Es ermöglicht derartige Kompositionen und erlaubt eine einheitliche Handhabung von individuellen und zusammengesetzten Objekten. Ein Composite enthält folgende Bestandteile: l Eine (oft abstrakte) Basisklasse, die sowohl zusammengesetzte als auch elementare Objekte repräsentiert. Diese Basisklasse wird auch als "Component" bezeichnet. l Alle elementaren Objekte sind aus dieser Basisklasse abgeleitet. l Daraus abgeleitet gibt es mindestens eine Containerklasse, die in der Lage ist, eine Menge von Objekten der Basisklasse aufzunehmen. Somit sind beide Bedingungen erfüllt. Der Container ermöglicht die Komposition der Objekte zu Baumstrukturen, und die Basisklasse stellt die einheitliche Schnittstelle für elementare Objekte und Container zur Verfügung. Das Klassendiagramm für ein Composite sieht so aus: AbstractComponent ConcreteComponent 1 ConcreteComponent 2 Container Klassendiagramm eines Composite Das folgende Listing skizziert dieses Design-Pattern am Beispiel einer einfachen Menüstruktur: 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 /* Listing1013.java */ class MenuEntry1 { protected String name; public MenuEntry1(String name) { this.name = name; } public String toString() { return name; } } 69 018 class IconizedMenuEntry1 extends MenuEntry1 020 { 021 private String iconName; 022 023 public IconizedMenuEntry1(String name, String iconName){ 025 super(name); 026 this.iconName = iconName; 027 } 028 } 029 030 class CheckableMenuEntry1 extends MenuEntry1 032 { 033 private boolean checked; 034 035 public CheckableMenuEntry1(String name, boolean checked) { 037 super(name); 038 this.checked = checked; 039 } 040 } 041 042 class Menu1 extends MenuEntry1 044 { 045 MenuEntry1[] entries; 046 int entryCnt; 047 048 public Menu1(String name, int maxElements) { 050 super(name); 051 this.entries = new MenuEntry1[maxElements]; 052 entryCnt = 0; 053 } 054 055 public void add(MenuEntry1 entry) { 057 entries[entryCnt++] = entry; 058 } 059 060 public String toString() 061 { 062 String ret = "("; 063 for (int i = 0; i < entryCnt; ++i) { 064 ret += (i != 0 ? ",": "") + entries[i].toString(); 065 } 066 return ret + ")"; 067 } 068 } 069 070 public class Listing1013 071 { 072 public static void main(String[] args) 073 { 074 Menu1 filemenu = new Menu1("Datei", 5); 075 filemenu.add(new MenuEntry1("Neu")); 076 filemenu.add(new MenuEntry1("Laden")); 077 filemenu.add(new MenuEntry1("Speichern")); 078 079 Menu1 confmenu = new Menu1("Konfiguration", 3); 080 confmenu.add(new MenuEntry1("Farben")); 081 confmenu.add(new MenuEntry1("Fenster")); 082 confmenu.add(new MenuEntry1("Pfade")); 083 filemenu.add(confmenu); 084 085 filemenu.add(new MenuEntry1("Beenden")); 086 087 System.out.println(filemenu.toString()); 088 } 089 } Listing 10.13: Das Composite-Pattern 70 Die Komponentenklasse hat den Namen MenuEntry1. Sie repräsentiert Menüeinträge und ist Vaterklasse der spezialisierten Menüeinträge IconizedMenuEntry1 und CheckableMenuEntry1. Zudem ist sie Vaterklasse des Containers Menu1, der Menüeinträge aufnehmen kann. Bestandteil der gemeinsamen Schnittstelle ist die Methode toString. In der Basisklasse und den elementaren Menüeinträgen liefert sie lediglich den Namen des Objekts. In der Containerklasse wird sie überlagert und liefert eine geklammerte Liste aller darin enthaltener Menüeinträge. Dabei arbeitet sie unabhängig davon, ob es sich bei dem jeweiligen Eintrag um einen elementaren oder einen zusammengesetzten Eintrag handelt, denn es wird lediglich die immer verfügbare Methode toString aufgerufen. Das Testprogramm erzeugt ein "Datei"-Menü mit einigen Elementareinträgen und einem Untermenü "Konfiguration" und gibt es auf Standardausgabe aus: (Neu,Laden,Speichern,(Farben,Fenster,Pfade),Beenden) 71 Observer-Muster Bei der objektorientierten Programmierung werden Programme in viele kleine Bestandteile zerlegt, die für sich genommen autonom arbeiten. Mit zunehmender Anzahl von Bausteinen steigt allerdings der Kommunikationsbedarf zwischen diesen Objekten, und der Aufwand, sie konsistent zu halten, wächst an. Ein Observer ist ein Design-Pattern, das eine Beziehung zwischen einem Subject und seinen Beobachtern aufbaut. Als Subjectwird dabei ein Objekt bezeichnet, dessen Zustandsänderung für andere Objekte interessant ist. Als Beobachter werden die Objekte bezeichnet, die von Zustandsänderungen des Subjekts abhängig sind; deren Zustand also dem Zustand des Subjekts konsistent folgen muß. Kurzbeschreibung Ermöglicht die dynamische Registrierung von Objektabhängigkeiten, so dass bei einem Zustandswechsel eines Objekts alle von ihm abhängigen Objekte benachrichtigt werden. Problembeschreibung In vielen Anwendungsbereichen soll sich der Zustandswechsel eines Objekts direkt auf den Zustand bzw. das Verhalten anderer Objekte auswirken. Beispiel : Ein Datenbestand und seine – u.U. gleichzeitige – Darstellung in verschiedenen Formaten in einem GUI-System (z. B. Tabelle, Balkendiagramm, Tortendiagramm). Jede Änderung des Datenbestandes muss sich umgehend auf alle Darstellungen auswirken. Eine enge Kopplung der beteiligten Objekte ( die beteiligten Objekte haben voneinander Kenntnis) ist meist nicht wünschenswert, da dadurch die unabhängige Verwendung und Modifikation ihrer jeweiligen Klassen stark eingeschränkt wird. Außerdem müssen in diesem Fall die Abhängigkeiten bereits zur Compilezeit bekannt sein, was eine flexible dynamische Anpassung zur Laufzeit verhindert. Problemlösung Bei dem Objekt, von dessen Zustand andere Objekte abhängen (Publisher-Objekt), wird eine Liste der abhängigen Objekte geführt. In diese Liste tragen sich alle Objekte, die über den Zustand dieses Objekts auf dem Laufenden gehalten werden wollen (weil sie von ihm abhängen), ein (Subscriber-Objekte, Observer-Objekte). Bei einem Wechsel seines Zustands informiert das Publisher-Objekt alle eingetragenen Observer-Objekt hierüber (notify). Diese können dann den neuen Zustand vom Publisher-Objekt ermitteln und entsprechend der eingetretenen Änderung reagieren (z.B. ihren eigenen Zustand an den Zustand des Publisher-Objektes anpassen). Die Anzahl der Observer-Objekte muss dem Publisher-Objekt a priori nicht bekannt sein. Bei ihm können sich zur Laufzeit beliebig viele Observer-Objekte an- bzw. auch wieder abmelden. Publisher-Objekt und Observer-Objekte sind von einander entkoppelt und können unabhängig voneinander modifiziert werden. Die Publisher-Schnittstelle wird durch eine geeignete Klasse (Publisher) zur Verfügung gestellt. Diese Klasse dient als Basisklasse für konkrete Publisher-Klassen (ConcretePublisher), die die Datenkomponenten für den jeweiligen Zustand und die Methoden zum Setzen und Ermitteln derselben bereitstellen. Das Interface zum Informieren von Observer-Objekten wird durch eine abstrakte Klasse (Observer) definiert Dieses Interface wird von konkreten Observer-Klassen (ConcreteObserver) implementiert. Objekte dieser Klassen enthalten – neben ihren eigentlichen Zustands-Datenkomponenten – eine Referenz auf das konkrete Publisher-Objekt, bei dem sie sich eingetragen haben. Publisher -observers:Observer +attach:Observer +detach:Observer +notify:void 0..n observers interface Observer +update:void void notify() { for all o in observers o.update(); } ConcretePublisher ConcreteObserver 1 -subjState:State publisher +setState:void +getState:State -observState:State -publisher:ConcretePublisher +update:void Abbildung 10.8: Klassendiagramm eines Observers 72 Anwendungskonsequenzen ▻ Publisher und Observer können unabhängig voneinander geändert und ausgetauscht werden. Publisher können wiederbenutzt werden, ohne ihre Observer wiederzubenutzen und umgekehrt. Observer können hinzugefügt werden, ohne den Publisher oder andere Observer zu verändern. ▻ Die Kopplung zwischen Publisher und Observer ist abstrakt und minimal. Ein Publisher-Objekt weiß lediglich, dass es eine Liste von Observer-Objekten besitzt, die das Observer-Interface implementieren. Es kennt nicht die konkrete Klasse seiner Observer. Publisher und Observer können unterschiedlichen Abstraktions-Ebenen (Schichtenmodell !) angehören. ▻ Observer-Objekte können gegebenenfalls selbst auch einen Zustandswechsel beim Publisher-Objekt bewirken ▻ Die Information über den Zustandswechsel (Notifikation) durch das Publisher-Objekt ist automatisch eine BroadcastKommunikation. Es werden immer alle eingetragenen Observer-Objekte informiert, unabhängig davon, wieviel es sind. Es obliegt einem Observer-Objekt, eine Notifikation gegebenenfalls zu ignorieren. ▻ Falls sehr viele Observer-Objekte eingetragen sind, kann eine Notifikation und der dadurch ausgelöste Observer-Update u.U. eine längere Zeit dauern. ▻ Da das eine Zustandsänderung bewirkende Objekt keine Information über die Observer und die mit diesen verbundenen "Update"-Kosten hat, können "leichtfertige" Zustandsänderungen einen erheblichen zeit- und damit kostenintensiven Aufwand bewirken. ▻ Das einfache Notifikations-Protokoll liefert keine Informationen darüber, was sich im Publisher-Objekt geändert hat. Dies festzustellen, obliegt dem Observer-Objekt, was den Update-Aufwand gegebenenfalls noch erhöht. Als Alternative kann u.U. ein erweitertes Protokoll definiert werden, dass detailliertere Zustandsänderungs-Info liefert. ▻ Es gibt Fälle, bei denen sinnvoll ist, dass sich ein Observer-Objekt bei mehreren Publisher-Objekten für eine Notifikation einträgt. In diesen Fällen muss die Update-Botschaft eine Information über das absendende PublisherObjekt enthalten 73 Das folgende Listing zeigt eine beispielhafte Implementierung: 001 /* Listing1015.java */ 002 package observer; 003 interface Observer 004 { 005 public void update(Subject subject); 006 } 007 008 class Subject //Publisher 009 { 010 Observer[] observers = new Observer[5]; 011 int observerCnt = 0; 012 013 public void attach(Observer observer) 014 { 015 observers[observerCnt++] = observer; 016 } 017 018 public void detach(Observer observer) 019 { 020 for (int i = 0; i < observerCnt; ++i) { 021 if (observers[i] == observer) { 022 --observerCnt; 023 for (;i < observerCnt; ++i) { 024 observers[i] = observers[i + 1]; 025 } 026 break; 027 } 028 } 029 } 030 031 public void fireUpdate() 032 { 033 for (int i = 0; i < observerCnt; ++i) { 034 observers[i].update(this); 035 } 036 } 037 } //Concrete Publisher (Subject) class Counter extends Subject{ int cnt = 0; public void inc() { ++cnt; fireUpdate(); } } //class Listing1015 ist Applikationsklasse und konkreter Observer durch Implementierung des Interfaces //Observer als anonyme Klasse public class Listing1015 { public static void main(String[] args) { Counter counter = new Counter(); counter.attach( new Observer() { public void update(Subject subject){ System.out.println("divisible by 3: "); } }); while (counter.cnt < 10) { counter.inc(); System.out.println(counter.cnt); } } } Listing: Das Observer-Pattern 74 Als konkreter Publisher wird hier die Klasse Counter verwendet. Sie erhöht bei jedem Aufruf von inc den eingebauten Zähler um eins und informiert alle registrierten Beobachter, falls der neue Zählerstand durch drei teilbar ist. Im Hauptprogramm instanzieren wir ein Counter-Objekt und registrieren eine lokale anonyme Klasse als Listener, die bei jeder Benachrichtigung eine Meldung ausgibt. Während des anschließenden Zählerlaufs von 1 bis 10 wird sie dreimal aufgerufen: 1 2 divisible by 3: 3 4 5 divisible by 3: 6 7 8 divisible by 3: 9 10 Das Observer-Pattern ist in Java sehr verbreitet, denn die Kommunikation zwischen graphischen Dialogelementen und ihrer Anwendung basiert vollständig auf dieser Idee. Allerdings wurde es etwas erweitert, die Beobachter werden als Listener bezeichnet, und es gibt von ihnen eine Vielzahl unterschiedlicher Typen mit unterschiedlichen Aufgaben. Da es zudem üblich ist, daß ein Listener sich bei mehr als einem Subjekt registriert, wird ein Aufruf von update statt des einfachen Arguments jeweils ein Listener-spezifisches Ereignisobjekt übergeben. Darin werden neben dem Subjekt weitere spezifische Informationen untergebracht. Zudem haben die Methoden gegenüber der ursprünglichen Definition eine andere Namensstruktur, und es kann sein, daß ein Listener nicht nur eine, sonderen mehrere unterschiedliche Update-Methoden zur Verfügung stellen muß, um auf unterschiedliche Ereignistypen zu reagieren. Das Listener-Konzept von Java wird auch als Delegation Based Event Handling bezeichnet. Hinweis 75