Softwaretechnologie, © Prof. Uwe Aßmann
Technische Universität Dresden, Fakultät Informatik 1
14) Architektur interaktiver Systeme
Prof. Dr. rer. nat. habil. Uwe Aßmann Institut für Software- und
Multimediatechnik Lehrstuhl Softwaretechnologie
Fakultät für Informatik TU Dresden Version 11-0.1, 14.05.11
1. Übersicht über die Architektur
2. Einführendes Beispiel für Java-AWT-Oberflächen 3. Ereignisgesteuerter
Programmablauf Play-In 4. Benutzungsoberflächen mit
Swing
Prof. U. Aßmann, Softwaretechnologie 2
Andere Literatur
►
[Herrmann] M. Veit, S. Herrmann. Model-View-Controller and Object Teams: A Perfect Match of Paradigms. Aspect-Oriented System Development (AOSD) 2003, ACM Press
Softwaretechnologie, © Prof. Uwe Aßmann
Technische Universität Dresden, Fakultät Informatik 3
14.1 Kopplung von Graphischen Benutzeroberflächen (GUI) und Anwendungslogik
.. Bringe Ereignisse, “auslösende”
Fensterelemente (Sicht) und Modell zusammen
Prof. U. Aßmann, Softwaretechnologie 4
Schichtenarchitektur
Benutzungsoberfläche - Teile des Kontextmodells
Window Button
Menu
(Fachliches) Modell Teammitglied
Teambesprechung
Ablauf- steuerung
Datenhaltung
z.B. relationale Datenbank
Prof. U. Aßmann, Softwaretechnologie 5
Schichtenarchitektur
Graphische Benutzungsoberfläche
(Fensteroberfläche, Widget-Hierarchie)
Window
Button Menu
(Fachliches) Modell
Teammitglied Teambesprechung
Unsichtbare Ablauf- Steuerung
(Ereignis- Verwaltung) Play-In
(asynchron)
Play-out (synchron, aber multiple Sichten und pull-Steuerung)
Prof. U. Aßmann, Softwaretechnologie 6
Graphische Benutzungsoberflächen Graphical User Interfaces (GUI)
►
1980: Smalltalk-80-Oberfläche (Xerox)
►
1983/84: Lisa/Macintosh-Oberfläche (Apple)
►
1988: NextStep (Next)
►
1989: OpenLook (Sun)
►
1989: Motif (Open Software Foundation)
►
1987/91: OS/2 Presentation Manager (IBM)
►
1990: Windows 3.0 (Microsoft)
►
1995-2001: Windows 95/NT/98/2000/ME/XP (Microsoft)
►
1995: Java AWT (SunSoft)
►
1997: Swing Components for Java (SunSoft)
►
2002: SWT von Eclipse
►
2006: XAML, Silverlight (Microsoft)
■ Xswt, xswing, XUL (Mozilla) etc.
Prof. U. Aßmann, Softwaretechnologie 7
Programme mit GUI laufen in 2 Phasen
1) Aufbauphase: Aufbau der Fensteroberflächen (-fronten, widget
hierarchies) durch Konstruktoraufrufe und Einfügen in
Fensterhierarchie (embodiment)
2) Reaktionsphase,1)bei der die Benutzeraktionen vom System (Ereignisverwaltung) als Ereignisobjekte ins Programm gegeben werden (Play-In)
2)Bei der in der Anwendungslogik durchgeführten Aktionen die Fensteroberfläche auf den neuesten Stand gebracht wird (Play-Out)
Prof. U. Aßmann, Softwaretechnologie 8
Widgets und Datenstrukturen
►
Fensterstrukturen sind hierarchisch (Einkapselung von Widgets)
►
Datenstruktur in Anwendung wird den Widget-Hierarchien zugeordnet
►
Screen-Buffer zeigt die Widget-Struktur bitweise (
paint())
Screen Buffer
View 1 - Widget Tree 1 (Fenster-Struktur)
View 2 - Widget Tree 2 (Fenster-Struktur)
Modell: Datenstruktur in Anwendungslogik
(und Datenbank) widget-
Observer
jdk- Observer
Prof. U. Aßmann, Softwaretechnologie 9
Zusammenspiel der Widget-Struktur und der Anwendungslogik
►
Interaktive Anwendungen mit GUI laufen in Phasen:
1)Netzaufbau-Phase:
♦ Aufbau der Fensterstrukturen des GUI (widget hierarchies)
♦ Aufbau der Datenstrukturen in der Anwendungslogik (Modellstruktur)
♦ Anschluß des GUI-Reaktionscodes auf Veränderungen der Modellstruktur (View wird Observer des Modells, Vorbereitung des Play-Out, callbacks)
♦ Anschluß des Modell-Reaktionscode auf Benutzereingaben (Controller ist Observer der Widgets, Vorbereitung des Play-In)
2)Arbeitsphase (Reaktive Phase)
♦ bei der die Benutzeraktionen vom System als Ereignisobjekte ins Programm gegeben werden, d.h. die Reaktionsobjekte des Modells als Listener benachrichtigt und ausgeführt werden (Play-In)
♦ Bei denen die GUI-Views auf Modelländerungen durch Neuanzeigen reagieren (Play-Out)
►
Der Steuerfluß eines GUI-Programms wird nie explizit spezifiziert, sondern ergibt sich aus den Aktionen des Benutzers
■ Das GUI-Programm reagiert nur (reaktives System)
Prof. U. Aßmann, Softwaretechnologie 10
Netzaufbauphase: Aufbau der Widget-Struktur
►
Verschiedene Techniken:
■ Durch Konstruktoraufrufe und Additionen von Unterwidgets zu Oberwidgets (encapsulation)
♦ rein in Java-AWT/Swing, mit expliziter Konstruktion der Widget-Hierarchien
■ Durch einen XML-Baum, der von einem XML-Parser eingelesen und als Objekt-Struktur im Speicher abgelegt wird (XUL - Firefox, XAML - Vista)
■ Durch einen HTML-Baum, der von einem Brauser interpretiert wird (für Webanwendungen)
■ Durch einen HTML-Baum, der bei Veränderungen inkrementell im Brauser nachgeladen wird (Web 2.0, Ajax)
Screen Buffer
View 1 - Widget Tree 1 in html / Ajax
Modell: Datenstruktur in Anwendungslogik
(und Datenbank) Client Server
Softwaretechnologie, © Prof. Uwe Aßmann
Technische Universität Dresden, Fakultät Informatik 11
14.2 Einführendes Beispiel über Play-In und Play-Out mit Java-AWT
Prof. U. Aßmann, Softwaretechnologie 12
Sichten: Zähler als motivierendes Beispiel
c: Counter ctr = 7
Counter
+ count() + reset() + getValue()
– ctr
Modell
cf: CounterFrame
Sicht 1
Sicht 2Prof. U. Aßmann, Softwaretechnologie 13
Modell und Sicht
Methodenaufrufe
=> Änderungen (play-in, asynchron ausgelöst)
Beispiele: Verschiedene Dokumentenansichten, Statusanzeigen, Verfügbarkeit von Menüpunkten
Frage:
Wie hält man das Modell in der Anwendungslogik unabhängig von den beliebig vielen Sichten darauf ?Benachrichtigung vieler Sichten über Änderungen (play-out, synchron, viele Listener) Fachliche Klasse
(Modell)
Sicht 1
(View) Sicht 2
(View)
Muster "Observer"
Controller
Prof. U. Aßmann, Softwaretechnologie 14
Ein Zähler (Beispiel für fachliches Modell)
class Counter { private int ctr = 0;
public void count () { ctr++;
}public void reset () { ctr = 0;
}public int getValue () { return ctr;
} }
Prof. U. Aßmann, Softwaretechnologie 15
Beobachtbares Modell (Play out, Model)
class Counter extends Observable { private int ctr = 0;
public void count () { ctr++;
setChanged();
notifyObservers();
}
public void reset () { ctr = 0;
setChanged();
notifyObservers();
}
public int getValue () { return ctr;
} }
• Counter wird Subjekt im Muster Observer; bei Veränderung des Counter wird die Oberfl äche benachrichtigt (play out)
• Das fachliche Modell enthält keinerlei Bezug auf die Benutzungsoberfl äche
• Beliebig viele „Listener“ in Oberfl äche möglich
Prof. U. Aßmann, Softwaretechnologie 16
Hauptprogramm für Fensteranzeige
import java.awt.*;
class ExampleFrame extends Frame { public ExampleFrame () {
setTitle("untitled");
setSize(150, 50);
setVisible(true);
} }
class GUI1 {
public static void main (String[] argv) { // Phase 1: Aufbau der Fensteroberfläche
ExampleFrame f = new ExampleFrame();
// .. implizites Betreten der Reaktionsschleife:
// Phase 2: reaktives Programm }
}
Prof. U. Aßmann, Softwaretechnologie 17
Registrierung für Listener (Play-In)
►
Im „Auslöser“ java.awt.Frame findet sich eine
Registrierungsprozedur (ererbt von java.awt.Window):
public class Frame ... {
public void addWindowListener (WindowListener l)
}
►
java.awt.event.WindowListener ist eine Schnittstelle:
public interface WindowListener { ... Methoden zur Ereignisbehandlung }
►
Vergleich mit Observer-Muster:
■ Frame bietet einen "Observable (Subject)"-Mechanismus
■ Window-Listener ist eine "Observer"-Schnittstelle
Observer Observable (Subject)
Observing (Observer)
Prof. U. Aßmann, Softwaretechnologie 18
Hauptprogramm für schließbares Fenster (play-in)
import java.awt.*;
import java.awt.event.*;
class WindowCloser implements WindowListener { ... siehe später ...
}
class ExampleFrame extends Frame { public ExampleFrame () {
setTitle("untitled");
setSize(150, 50);
addWindowListener(new WindowCloser());
setVisible(true);
} }
class GUI2 {
public static void main (String[] argv) { ExampleFrame f = new ExampleFrame();
} }
Anmelden
Subjekt starten (Ereignisverwaltung)
reagieren (Phase 2)
Softwaretechnologie, © Prof. Uwe Aßmann
Technische Universität Dresden, Fakultät Informatik 19
I claim not to have controlled events,
but confess plainly that events have controlled me.
Abraham Lincoln, 1864
14.3 Ereignisgesteuerter
Programmablauf beim Play-In
Asynchroner Programmablauf beim Play-In
Prof. U. Aßmann, Softwaretechnologie 20
Ereignisse
►
Ein Ereignis ist ein Vorgang in der Umwelt des Softwaresystems von vernachlässigbarer Dauer, der für das System von Bedeutung ist.
Eine wichtige Gruppe von Ereignissen sind Benutzeraktionen:
■ Drücken eines Knopfs
■ Auswahl eines Menüpunkts
■ Verändern von Text
■ Zeigen auf ein Gebiet
■ Schließen eines Fensters
■ Verbergen eines Fensters
■ Drücken einer Taste
■ Mausklick
Prof. U. Aßmann, Softwaretechnologie 21
Schliessknopf
Beispiel für Ereignisverarbeitung
►
Das erste Java-Programm in der Vorlesung mit einer
"graphischen Benutzungsoberfläche"...
– Hinweis "für Java-Insider": Hier noch reines "AWT", im nächsten Kapitel wird "Swing" eingeführt werden.
• Aufgabe: Ein leeres, aber schliessbares Fenster anzeigen
Fensterdarstellung ("look and feel") gemäß Windows:
Prof. U. Aßmann, Softwaretechnologie 22
Ereignis-Klassen und ihre “auslösenden”
Oberflächenelemente
►
Ereignis-Klassen in (Java-)Benutzungsoberflächen:
■ WindowEvent
■ ActionEvent
■ MouseEvent
■ KeyEvent, ...
►
Bezogen auf Klassen für Oberflächenelemente
■ Window
■ Frame
■ Button
■ TextField, ...
►
Zuordnung (Beispiele):
■ Window (mit Frame) erzeugt WindowEvent
♦ z.B. Betätigung des Schliessknopfes
■ Button erzeugt ActionEvent
♦ bei Betätigung des Knopfes
Prof. U. Aßmann, Softwaretechnologie 23
Ereignis-Bearbeitung beim play-in (1)
Benutzer
Laufzeit-System (Ereignisverwaltung)betätigt Schliessknopf
►
Reaktion auf ein Ereignis durch Programm:
■ Ereignis wird vom Laufzeitsystem erkannt
►
Programm soll von technischen Details entkoppelt werden
■ Beobachter-Prinzip:
♦ Programmteile registrieren sich für bestimmte Ereignisse
♦ Laufzeitsystem sorgt für Aufruf an passender Stelle
►
Objekte, die Ereignisse beobachten, heißen bei Java Listener.
Prof. U. Aßmann, Softwaretechnologie 24
Ereignis-Bearbeitung beim play-in (2)
l: WindowListener registriert bei
Delegation:
Aufruf einer Methode von l mit Argument e
e: WindowEvent Quelle
Benutzer
Laufzeit-Systembetätigt Schliessknopf
f: Frame
vom System (Ereignisverw.) neu erzeugt:
Prof. U. Aßmann, Softwaretechnologie 25
Hierarchie der AWT EventListener (Widget- Listeners) und Event-Objekte
►
Die Vererbungshierarchien EventListener und AWTEvent werden parallel variiert
WindowListener TextListener
void TextValueChanged(TextEvent e) AWTEventListener
void eventDispatched(AWTEvent event)
ChangeListener
void stateChanged(ChangeEvent e)
WindowFocusListener
void windowGainedFocus(WindowEvent e) void windowLostFocus(WindowEvent e) EventListener
ActionListener
void actionPerformed(ActionEvent e) MenuListener
void menuSelected(MenuEvent e) void menuCancelled(MenuEvent e) void menuDeselected(MenuEvent e)
PropertyChangeListener
void propertyChange(PropertyChangeEvent evt)
WindowEvent TextEvent
ChangeEvent AWTEvent
ActionEvent
MenuEvent
PropertyChangeEvent EventObject
Prof. U. Aßmann, Softwaretechnologie 26
java.awt.event.WindowListener
/** empty marker interface, from which all listeners have to inherit */
public interface EventListener { };
public interface WindowListener extends EventListener {
public void windowClosed (WindowEvent ev);
public void windowOpened (WindowEvent ev);
public void windowIconified (WindowEvent ev);
public void windowDeiconified (WindowEvent ev);
public void windowActivated (WindowEvent ev);
public void windowDeactivated (WindowEvent ev);
public void windowClosing (WindowEvent ev);
}
java.util.EventListener:Basisinterface für alle "Listener"
Prof. U. Aßmann, Softwaretechnologie 27
Definition von Ereignissen:
java.awt.event.WindowEvent
public class WindowEvent extends AWTEvent { ...
// Konstruktor, wird vom System aufgerufen public WindowEvent (Window source, int id);
// Abfragemöglichkeiten public Window getWindow();
...
}
Prof. U. Aßmann, Softwaretechnologie 28
java.awt.event.ActionEvent, ActionListener
public class ActionEvent extends AWTEvent { ...
// Konstruktor, wird vom System // (Ereignisverwaltung) aufgerufen public ActionEvent
(Window source, int id, String command);
// Abfragemöglichkeiten
public Object getSource ();
public String getActionCommand();
...
}
public interface ActionListener extends EventListener {
public void actionPerformed (ActionEvent ev);
}
Prof. U. Aßmann, Softwaretechnologie 29
Wer reagiert denn hier auf Ereignisse?
►
Ereignis-Listener sind Schnittstellen, keine Implementierungsklassen
►
Wie programmiert man die Klassen, die die abhörenden Schnittstellen implementieren?
■ Eine neue Klasse (Implementierungsklasse)
■ Eine Default-Implementierung benutzen (WindowAdapter)
■ Eine Unterklasse der Default-Implementierung WindowAdapter
■ Eine innere Klasse
■ Eine anonyme Klasse
Prof. U. Aßmann, Softwaretechnologie 30
a) Implementierungsklasse WindowCloser für Ereignis "Schließen"
import java.awt.*;
import java.awt.event.*;
class WindowCloser implements WindowListener { // Reagiert nur auf Schließen
public void windowClosed (WindowEvent ev) {}
public void windowOpened (WindowEvent ev) {}
public void windowIconified (WindowEvent ev) {}
public void windowDeiconified (WindowEvent ev) {}
public void windowActivated (WindowEvent ev) {}
public void windowDeactivated (WindowEvent ev) {}
public void windowClosing(WindowEvent event) { System.exit(0);
} }
Prof. U. Aßmann, Softwaretechnologie 31
Hauptprogramm für schließbares Fenster
import java.awt.*;
import java.awt.event.*;
class WindowCloser implements WindowListener { ... siehe vorige Folie ...
}
class ExampleFrame extends Frame { public ExampleFrame () {
setTitle("untitled");
setSize(150, 50);
addWindowListener(new WindowCloser());
setVisible(true);
} }
class GUI2 {
public static void main (String[] argv) { ExampleFrame f = new ExampleFrame();}
}
Prof. U. Aßmann, Softwaretechnologie 32
b) Vereinfachung 1: Unterklasse der Default- Implementierung WindowAdapter
►
WindowAdapter bietet eine Default-Implementierung für die WindowListener-Funktionen an:
package java.awt.event;
public abstract class WindowAdapter implements WindowListener {
public void windowClosed (WindowEvent ev) {}
public void windowOpened (WindowEvent ev) {}
public void windowIconified (WindowEvent ev) {}
public void windowDeiconified (WindowEvent ev) {}
public void windowActivated (WindowEvent ev) {}
public void windowDeactivated (WindowEvent ev) {}
public void windowClosing (WindowEvent ev) {}
}
Prof. U. Aßmann, Softwaretechnologie 33
Vereinfachung 1: Unterklasse der Default- Implementierung WindowAdapter
►
Redefinition einer leeren Reaktionsmethode:
import java.awt.*;
import java.awt.event.*;
class WindowCloser extends WindowAdapter {
public void windowClosing(WindowEvent event) { System.exit(0);
} }
class ExampleFrame extends Frame { public ExampleFrame () {
setTitle("untitled");
setSize(150, 50);
addWindowListener(new WindowCloser());
setVisible(true);
} }
class GUI3 {
public static void main (String[] argv) { ExampleFrame f = new ExampleFrame();}
} Prof. U. Aßmann, Softwaretechnologie 34
Vereinfachung 1: Unterklasse der
DefaultImplementierung WindowAdapter
WindowCloser
registriert bei
Frame
ExampleFrame
setSize setTitle setVisible
Window
addWindowListener (l: WindowListener)
<<interface>>
WindowListener
windowClosing (e: WindowEvent)
WindowEvent
<<use>>
WindowAdapter
Prof. U. Aßmann, Softwaretechnologie 35
Diskussion zum Begriff "Adapter"
<<interface>>WindowListener windowClosing (e: WindowEvent) window... ()...
...
WindowAdapter
Adapter
WindowCloser
windowClosing (e: WindowEvent)
Ist das eine Anwendung des Adapter-Musters?
Target Adaptee
Adapter
.. sollte besser DefaultWindowListener
heissen..
Prof. U. Aßmann, Softwaretechnologie 36
b) Vereinfachung 2: Innere Klasse benutzen
import java.awt.*;
import java.awt.event.*;
class ExampleFrame extends Frame {
/* inner */ class WindowCloser extends WindowAdapter { public void windowClosing(WindowEvent event) {
System.exit(0);
} }
public ExampleFrame () { setTitle("untitled");
setSize(150, 50);
addWindowListener(new WindowCloser());
setVisible(true);
} }
class GUI4 {
public static void main (String[] argv) { ExampleFrame f = new ExampleFrame();}}
Prof. U. Aßmann, Softwaretechnologie 37
c) Vereinfachung 3: Anonyme Klasse benutzen
import java.awt.*;
import java.awt.event.*;
class ExampleFrame extends Frame { public ExampleFrame () {
setTitle("untitled");
setSize(150, 50);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) { System.exit(0);
}});
setVisible(true);
} }
class GUI5 {
public static void main (String[] argv) { ExampleFrame f = new ExampleFrame();
}}
Softwaretechnologie, © Prof. Uwe Aßmann
Technische Universität Dresden, Fakultät Informatik 38
14.4 Aufbau der Benutzungsoberfläche (Widget-Hierarchie) mit Swing
Prof. U. Aßmann, Softwaretechnologie 39
Hier: Expliziter Aufbau mit AWT und Swing
►
Abstract Window Toolkit (AWT):
■ Seit Version 1.1 Standard-Bestandteil von Java
■ Umfangreiche Bibliothek von Oberflächen-Bausteinen
■ Plattformunabhängige Schnittstellen, aber grosse Teile plattformspezifisch realisiert ("native code")
►
Swing-Bibliotheken
■ Seit Version 1.2 Standard-Bestandteil von Java
■ Noch umfangreichere Bibliothek von Oberfächen-Bausteinen
■ Plattformunabhängiger Code
(d.h. Swing ist weitestgehend selbst in Java realisiert)
■ Wesentlich größerer Funktionsumfang
(nicht auf den "kleinsten Nenner" der Plattformen festgelegt)
Prof. U. Aßmann, Softwaretechnologie 40
Bibliotheken von AWT und Swing
►
Wichtigste AWT-Pakete:
■ java.awt: u.a. Grafik, Oberflächenkomponenten, Layout-Manager
■ java.awt.event: Ereignisbehandlung
■ Andere Pakete für weitere Spezialzwecke
►
Wichtigstes Swing-Paket:
■ javax.swing: Oberflächenkomponenten
■ Andere Pakete für Spezialzwecke
■ Viele AWT-Klassen werden auch in Swing verwendet!
►
Standard-Import-Vorspann:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
►
(Naiver) Unterschied zwischen AWT- und Swing-Komponenten:
■ AWT: Button, Frame, Menu, ...
■ Swing: JButton, JFrame, JMenu, ...
Prof. U. Aßmann, Softwaretechnologie 41
AWT/Swing-Klassenhierarchie (Ausschnitt)
lang.Object awt.Component
awt.Container
swing.JComponent awt.Window
swing.
JFrame swing.
JTextField swing.
JLabel swing.
JButton swing.
JPanel swing.
JTextComponent
►
Dies ist nur ein sehr kleiner Ausschnitt
■ Präfixe "java." und "javax." hier weggelassen.
Prof. U. Aßmann, Softwaretechnologie 42
Component, Container, Window, Frame, Panel
► awt.Component (abstrakt):
■ Oberklasse aller Bestandteile der Oberfläche public void setSize (int width, int height);
public void setVisible (boolean b);
► awt.Container (abstrakt):
■ Oberklasse aller Komponenten, die andere Komponenten enthalten public void add (Component comp);
public void setLayout (LayoutManager mgr);
► awt.Window
■ Fenster ohne Rahmen oder Menüs public void pack (); //Größe anpassen
► swing.JFrame
■ Größenveränderbares Fenster mit Titel public void setTitle (String title);
► swing.JPanel
■ Zusammenfassung von Swing-Komponenten
Component Container
Window JFrame JPanel
Prof. U. Aßmann, Softwaretechnologie 43
JComponent
►
Oberklasse aller in der Swing-Bibliothek neu implementierten, verbesserten Oberflächenkomponenten. Eigenschaften u.a.:
■ Einstellbares "Look-and-Feel"
■ Komponenten kombinierbar und erweiterbar
■ Rahmen für Komponenten
void setBorder (Border border);
(Border-Objekte mit BorderFactory erzeugbar)
■ ToolTips -- Kurzbeschreibungen, die auftauchen, wenn der Cursor über der Komponente liegt
void setToolTipText (String text);
■ Automatisches Scrolling
►
Beispiele für weitere Unterklassen von JComponent:
■ JList: Auswahlliste
■ JComboBox: "Drop-Down"-Auswahlliste mit Texteingabemöglichkeit
■ JPopUpMenu: "Pop-Up"-Menü
■ JFileChooser: Dateiauswahl
Component Container JComponent
Prof. U. Aßmann, Softwaretechnologie 44
Zähler-Beispiel: Grobentwurf der Oberfläche
CounterFrame cf abgeleitet von JFrame
JPanel valuePanel
JPanel buttonPanel
Prof. U. Aßmann, Softwaretechnologie 45
Die Sicht (View) mit Swing:
Gliederung, 1. Versuch
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
// .. value panel hinzufügen
getContentPane().add(valuePanel, BorderLayout.NORTH);
buttonPanel.add(countButton);
buttonPanel.add(resetButton);
buttonPanel.add(exitButton);
// .. button panel hinzufügen
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
pack();
setVisible(true);
}}
Subjekt starten (Ereignisverwaltung)
Prof. U. Aßmann, Softwaretechnologie 46
Hinzufügen von Komponenten zu JFrames
►
Ein JFrame ist ein "Container", d.h. dient zur Aufnahme weiterer Elemente.
►
Ein JFrame ist intern in verschiedene "Scheiben" (panes) organisiert. Die wichtigste ist die content pane.
• In JFrame ist definiert:
Container getContentPane();
glass panelayered pane menu barcontent pane
Prof. U. Aßmann, Softwaretechnologie 47
Die Sicht (View): Gliederung, 2. Versuch
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JPanel buttonPanel = new JPanel();
public CounterFrame (Counter c) { setTitle("SwingCounter");
getContentPane().add(valuePanel);
getContentPane().add(buttonPanel);
pack();
setVisible(true);
}}
Prof. U. Aßmann, Softwaretechnologie 48
Zähler-Beispiel: Entwurf der Wertanzeige
JPanel valuePanel
JLabel
valueLabel JTextField
valueDisplay
Prof. U. Aßmann, Softwaretechnologie 49
TextComponent, TextField, Label, Button
► JTextComponent:
■ Oberklasse von JTextField und JTextArea public void setText (String t);
public String getText ();
public void setEditable (boolean b);
► JTextField:
■ Textfeld mit einer Zeile
public JTextField (int length);
► JLabel:
■ Einzeiliger unveränderbarer Text public JLabel (String text);
► JButton:
■ Druckknopf mit Textbeschriftung public JButton (String label);
Component Container JComponent
JTextComponent JTextField
JLabel JButton
Prof. U. Aßmann, Softwaretechnologie 50
Die Sicht (View): Elemente der Wertanzeige
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
getContentPane().add(valuePanel);
getContentPane().add(buttonPanel);
pack();
setVisible(true);
}}
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
getContentPane().add(valuePanel);
getContentPane().add(buttonPanel);
pack();
setVisible(true);
}}
Prof. U. Aßmann, Softwaretechnologie 51
Zähler-Beispiel: Entwurf der Bedienelemente
JPanel buttonPanel
JButton
countButton JButton
resetButton JButton exitButton
Prof. U. Aßmann, Softwaretechnologie 52
Die Sicht (View): Bedienelemente
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
getContentPane().add(valuePanel);
buttonPanel.add(countButton);
buttonPanel.add(resetButton);
buttonPanel.add(exitButton);
getContentPane().add(buttonPanel);
pack();
setVisible(true);
}}
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
getContentPane().add(valuePanel);
buttonPanel.add(countButton);
buttonPanel.add(resetButton);
buttonPanel.add(exitButton);
getContentPane().add(buttonPanel);
pack();
setVisible(true);
}}
Prof. U. Aßmann, Softwaretechnologie 53
Layout-Manager
► Definition Ein Layout-Manager ist ein Objekt, das Methoden
bereitstellt, um die graphische Repräsentation verschiedener Objekte innerhalb eines Container-Objektes anzuordnen.
►
Formal ist LayoutManager ein Interface, für das viele Implementierungen möglich sind.
►
In Java definierte Layout-Manager (Auswahl):
■ FlowLayout (java.awt.FlowLayout)
■ BorderLayout (java.awt.BorderLayout)
■ GridLayout (java.awt.GridLayout)
►
In awt.Component:
public void add (Component comp, Object constraints);
erlaubt es, zusätzliche Information (z.B. Orientierung, Zeile/Spalte) an den Layout-Manager zu übergeben
Prof. U. Aßmann, Softwaretechnologie 54
Flow-Layout
►
Grundprinzip:
■ Anordnung analog Textfluß:
von links nach rechts und von oben nach unten
►
Default für Panels
■ z.B. in valuePanel und buttonPanel für Hinzufügen von Labels, Buttons etc.
►
Parameter bei Konstruktor: Orientierung auf Zeile, Abstände
►
Constraints bei add : keine
1 2 3 4 5
6
Prof. U. Aßmann, Softwaretechnologie 55
Border-Layout
►
Grundprinzip:
■ Orientierung nach den Seiten (N, S, W, O) bzw. Mitte (center)
►
Default für Window, Frame
■ z.B. in CounterFrame
für Hinzufügen von valuePanel, buttonPanel
►
Parameter bei Konstruktor: Keine
►
Constraints bei add:
– BorderLayout.NORTH, SOUTH, WEST, EAST, CENTER
Oberer ("Nord")-Bereich
(z.B. valuePanel)
Unterer ("Süd")-Bereich(z.B. buttonPanel)
Prof. U. Aßmann, Softwaretechnologie 56
Grid-Layout
►
Grundprinzip:
■ Anordnung nach Zeilen und Spalten
►
Parameter bei Konstruktor:
■ Abstände, Anzahl Zeilen, Anzahl Spalten
►
Constraints bei add: Keine
1,1 1,2 1,3
2,1 2,2 2,3
Prof. U. Aßmann, Softwaretechnologie 57
class CounterFrame extends JFrame { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
getContentPane().add(valuePanel, BorderLayout.NORTH);
buttonPanel.add(countButton);
buttonPanel.add(resetButton);
buttonPanel.add(exitButton);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
pack();
setVisible(true);
}}
Die Sicht (View): Alle sichtbaren Elemente
Prof. U. Aßmann, Softwaretechnologie 58
Model-View-Controller-Architektur
c: Counter ctr = 7
Counter
+ count() + reset() + getValue()
– ctr
Model
cf: CounterFrame
View<<beobachtet>>
bc: ButtonController
Controller<<steuert>>
<<beobachtet>>
►
GUI-Reagiert-Auf-Modelländerungen: Observer (Play-Out)
►
Modell-Reagiert-Auf-View: Controller (Play-In)
Play-Out
Play-In
Prof. U. Aßmann, Softwaretechnologie 59
Zähler-Beispiel für das Play-Out:
Anbindung View an Model
class CounterFrame extends JFrame // View implements Observer {
...
JTextField valueDisplay = new JTextField(10);
...
// connecting model to view
public CounterFrame /*View*/ (Counter c /*Model*/) { ...
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
valueDisplay.setText(String.valueOf(c.getValue()));
...
c.addObserver(this); // Registring view at model pack();
setVisible(true);
} // view listens at model
public void update (Observable o, Object arg) { Counter c = (Counter) o;
valueDisplay.setText(String.valueOf(c.getValue()));
}}
Prof. U. Aßmann, Softwaretechnologie 60
Wdh: java.awt.event.ActionEvent, ActionListener
public class ActionEvent extends AWTEvent { ...
// Konstruktor wird vom System aufgerufen public Object getSource ()
public String getActionCommand() ...
}
public interface ActionListener extends EventListener {
public void actionPerformed (ActionEvent ev);
}
Prof. U. Aßmann, Softwaretechnologie 61 // Die Steuerung belauscht die Knöpfe
class ButtonController // Controller implements ActionListener {
Counter myCounter;
// widget-Observer
public void actionPerformed (ActionEvent event) { String cmd = event.getActionCommand();
if (cmd.equals("Count"))
myCounter.count(); // Aktion auf Modell if (cmd.equals("Reset"))
myCounter.reset(); // Aktion if (cmd.equals("Exit"))
System.exit(0); // Aktion }
public ButtonController (Counter c) { myCounter = c;
}}
// Die Steuerung belauscht die Knöpfe class ButtonController // Controller implements ActionListener {
Counter myCounter;
// widget-Observer
public void actionPerformed (ActionEvent event) { String cmd = event.getActionCommand();
if (cmd.equals("Count"))
myCounter.count(); // Aktion auf Modell if (cmd.equals("Reset"))
myCounter.reset(); // Aktion if (cmd.equals("Exit"))
System.exit(0); // Aktion }
public ButtonController (Counter c) { myCounter = c;
}}
Die Steuerung des Play-In mit dem Controller
► Zum Play-In wird der Controller ein Listener der View-Widgets (hier buttons)
► Die update-Methode heisst actionPerformed(ActionEvent), da die Ablaufsteuerung des JDK diese sucht und aufruft
Prof. U. Aßmann, Softwaretechnologie 62
Play-In, Zähler-Beispiel: Anbindung des Controllers an mehrere Views (Subjekte)
class CounterFrame extends JFrame { ...
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { ...
ButtonController bc = new ButtonController(c);
// controller listens at another view countButton.addActionListener(bc);
buttonPanel.add(countButton);
// controller listens at another view resetButton.addActionListener(bc);
buttonPanel.add(resetButton);
// controller listens at another view exitButton.addActionListener(bc);
buttonPanel.add(exitButton);
...
}}
Prof. U. Aßmann, Softwaretechnologie 63
Alles zusammen: CounterFrame (1)
class CounterFrame extends JFrame implements Observer { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
valueDisplay.setText(String.valueOf(c.getValue()));
getContentPane().add(valuePanel,BorderLayout.NORTH );ButtonController bc = new ButtonController(c);
countButton.addActionListener(bc);
buttonPanel.add(countButton);
resetButton.addActionListener(bc);
buttonPanel.add(resetButton);
exitButton.addActionListener(bc);
buttonPanel.add(exitButton);
getContentPane().add(buttonPanel,BorderLayout.SOUTH);
class CounterFrame extends JFrame implements Observer { JPanel valuePanel = new JPanel();
JTextField valueDisplay = new JTextField(10);
JPanel buttonPanel = new JPanel();
JButton countButton = new JButton("Count");
JButton resetButton = new JButton("Reset");
JButton exitButton = new JButton("Exit");
public CounterFrame (Counter c) { setTitle("SwingCounter");
valuePanel.add(new JLabel("Counter value"));
valuePanel.add(valueDisplay);
valueDisplay.setEditable(false);
valueDisplay.setText(String.valueOf(c.getValue()));
getContentPane().add(valuePanel,BorderLayout.NORTH );ButtonController bc = new ButtonController(c);
countButton.addActionListener(bc);
buttonPanel.add(countButton);
resetButton.addActionListener(bc);
buttonPanel.add(resetButton);
exitButton.addActionListener(bc);
buttonPanel.add(exitButton);
getContentPane().add(buttonPanel,BorderLayout.SOUTH);
Prof. U. Aßmann, Softwaretechnologie 64
Alles zusammen: CounterFrame (2)
addWindowListener(new WindowCloser());
c.addObserver(this);
pack();
setVisible(true);
}
public void update (Observable o, Object arg) { Counter c = (Counter) o;
valueDisplay.setText(String.valueOf(c.getValue()));
} }
class ButtonController implements ActionListener { ... (wie oben) ...
}
class WindowCloser implements WindowListener { ... (wie oben) ...
}
Prof. U. Aßmann, Softwaretechnologie 65
c:Controller c:Controller
Überblick MVC
c:Controller
v:View v:View v:View
m:Model 1
*
1 1
*
1
[Herrmann]
change messages observer
change messages observer user input observer
direct change messages
model read model
change
{xor}
{xor}
Prof. U. Aßmann, Softwaretechnologie 66
Probleme im MVC
►
Wiederverwendung von Sichten und Controller – wie?
■ Modell ist abstrakte Klasse (white-box framework mit Variationspunkt Modell)
■ Sicht ist Rolle der Modellklasse (open role framework)
►
Granularität der Ereignisse:
■ Welche Änderungen des Modells sollen en-bloc von Sichten übernommen werden?
Prof. U. Aßmann, Softwaretechnologie 67
Was haben wir gelernt?
►
GUI-Programme laufen in 2 Phasen:
■ Aufbau der Fensterfronten (widget hierarchies) durch Konstruktoraufrufe und Additionen (embodiment)
♦ Vorbereitung Play-Out: Anschluß des View-Reaktionscodes als jdk-Observer des Modells
♦ Vorbereitung Play-In: Anschluß des Controller als widget-Observer der Views
■ Reaktionsphase, bei der die Benutzeraktionen vom System als Ereignisobjekte ins Programm gegeben werden:
♦ der Controller als Listener benachrichtigt und ausgeführt werden (Play-In)
♦ die Views bzw. der Controller als Listener des Modells benachrichtigt werden (Play-Out)
►
Der Kontrollfluß eines GUI-Programms wird nie explizit spezifiziert, sondern ergibt sich aus den Aktionen des Benutzers
■ Die Views reagieren auf Ereignisse im Screenbuffer, die von der Ablaufsteuerung gemeldet werden
■ Der Controller auf Widget-Veränderungen im View
■ Die Views auf Veränderungen im Modell
Prof. U. Aßmann, Softwaretechnologie 68
The End
►