630 likes | 765 Views
Grundlagen der Informatik 1 Thema 21: GUI und verschachtelte Klassen. Prof. Dr. Max Mühlhäuser Dr. Guido Rößling. AWT und Swing. AWT = Abstract Window Toolkit Package java.awt Benutzt Steuerelemente des darunterliegenden Betriebssystems
E N D
Grundlagen der Informatik 1Thema 21: GUI und verschachtelte Klassen Prof. Dr. Max Mühlhäuser Dr. Guido Rößling
AWT und Swing • AWT = Abstract Window Toolkit • Package java.awt • Benutzt Steuerelemente des darunterliegenden Betriebssystems • Native Code (= direkt für die Maschine geschrieben, keine VM) • schnell • Aussehen (Look) hängt vom System ab: unterschiedliche Schriften, … • Portabilität eingeschränkt • Swing • Package javax.swing (Teil der Java Foundation Classes) • Merkmal: Swing-Klassen beginnen mit „J“: JButton, ... • Vollständig in Java programmiert, eigener Look • Baut auf AWT auf • Verwendet ein Minimum an systemspezifischen Komponenten
Komponenten • Einige oft verwendete Komponenten und für welche Interaktionsaufgaben sie verwendet werden • Anzeigen von Textund Symbolen • JLabel • Auslösen von Aktionen • JButton • JMenu • JMenuItem
Komponenten • Eingabe von Text • JTextField • Auswahl aus einer festen Menge von Optionen • JCheckBox • Auswahl aus einer variablen Menge • JList gegenseitig ausschließend: JRadioButton JComboBox Button oder TextField +ausklappbare Listbox
Menschliche Faktoren • Beim Entwurf interaktiver Programme gibt es neben der funktionalen Vollständigkeit und Korrektheit viele zusätzliche Aspekte zu beachten • Dazu gehört die Art und Weise der Interaktion und wie leicht sie zu erlernen und benutzen ist • Richtlinien für gute GUIs sind u.a.: • Vermeide Modi. Allgemein sinnvolle Operationen sollten immer verfügbar sein. • Biete einfache und durchgängige Interaktionssequenzen an. • Überfordere den Benutzer nicht durch zu viele Optionen. • Zeige bei jeder Interaktionsstufe klar die verfügbaren Optionen. • Gib dem Benutzer angemessene Rückmeldungen. • Gib dem Benutzer die Möglichkeit, Fehler problemlos rückgängig zu machen.
Anatomie einer GUI-Applikation • Komponenten • Container • Ereignisse • Listener • Layout • Legt die Anordnungder Komponenten fest • Look & Feel • Legt das Aussehender Komponenten fest • getrennt: Anwendungslogik GUI-Framework Netz von kooperierenden Objekten mit klar festgelegten Zuständigkeiten: Container Komponenten Ereignisse Methodenaufrufe auf Komponente Anwendungslogik Listener Zustand: wert=3
Fäden (Threads) • Sequentielle Programme • haben Anfang, definierte Ausführungssequenz und Ende. • Zu jedem Zeitpunkt ist genau eine Anweisung aktiv. • Ein Faden (Thread) ist ein einzelner sequentieller Kontrollfluss in einem Programm. • Nebenläufige Programme • Programme können mehrere Fäden besitzen. • Mehrere Programmteile können so quasi gleichzeitig ablaufen. Faden Programm Fäden Programm
Fäden (Threads) • Fäden (Threads)… • nutzen die Ressourcen des Prozesses mit, in dem sie ablaufen • besitzen keine eigenen Speichersegmente • verfügen über einen eigenen Registersatz (inkl. Programmzähler und Statusregister) sowie einen eigenen Stack • werden oft auch als leichtgewichtige Prozesse (lightweightprocesses) bezeichnet. • GUI-Applikationen besitzen neben dem Haupt-Thread einen sogenannten Event-Dispatching Thread • Dieser Thread ruft Methoden der Anwendung auf, wenn bestimmte Ereignisse auftreten (Callback) • Die Ereignisbehandlungsroutinen in der Anwendung werden sequentiell ausgeführt • Das Zeichnen von Komponenten wird ebenfalls im Kontext dieses Threads ausgeführt
Programmiermodell Anwendung GUI-Framework (SWING) main main Komponente einfügen AWT-EventQueue AWT-EventQueue Ereignis: Komponente zeichnen AWT-EventQueue Ereignis: Benutzereingabe
Fenster importjavax.swing.JFrame; publicclassGUITestextendsJFrame { // The width of the window in pixels public static final intWIDTH = 400; // The height of the window in pixels public static final intHEIGHT = 300; // Constructs a new window with a given title publicGUITest(String title) { super(title); } // Start test application. Creates a new window and displays it publicstaticvoidmain(String args[]) { // Construct a new window. It is initially invisible GUITesttheWindow = newGUITest("My first GUI Application"); // Setwidth and height of the window theWindow.setSize(WIDTH, HEIGHT); // Open the window theWindow.setVisible(true); System.out.println("Exiting main..."); } }
Fenster • Wir beobachten... • main() wird verlassen, das Programm läuft aber weiter. • Mehrere Fäden.Der Event-Dispatching Thread läuft weiter. • Klick auf Schließen-Button beendet das Programm nicht. • Ereignisverarbeitung fehlt.Genaueres dazu später.
Container • Container und Komponenten • JFrame: „top levelcontainer“ • benutzt Fenster vom Betriebssystem • JPanel: „intermediate container“ • dient zum Gruppieren und Anordnen von Komponenten • Verschachtelung möglich • JLabel, JButton, ...: „atomare Komponenten“ • präsentieren dem Benutzer Informationen • erlauben oft Interaktion und Eingabe (Steuerelemente) • Container-Hierarchie • Selbst die einfachste Anwendung besteht auseiner Hierarchie von Containern und Komponenten • (Darstellung vereinfacht. Die Container zwischen JFrame und JPanel werden hier nicht weiter betrachtet) JFrame JPanel JLabel JFrame ... JPanel JLabel
Komponenten • Hinzufügen von Komponenten • Der JFrame erzeugt automatisch ein JPanel,das „contentPane“. • Darin kann die Anwendung neueKomponenten einfügen. • Die Komponenten werden eingefügt, währenddas Fenster noch unsichtbar ist, also zwischendem Erstellen des Fenster-Objekts mit new undder Anzeige mit setVisible(true). • JLabels • stellen Text oder/und Symbole dar • sind passive Komponenten, erlauben keine Interaktion JFrame ... contentPane JPanel JLabel
Labels importjava.awt.Container; importjavax.swing.JFrame; importjavax.swing.JLabel; public classGUITestextendsJFrame { publicGUITest(String title) { super(title); // Retrieve the area where one can add elements Container pane = getContentPane(); // Create a new label that displays help information JLabel label = newJLabel( "Press the [X] in the top right corner to exit"); // Add the label to the content of the window pane.add(label); } // ... }
Buttons importjava.awt.Container; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { publicGUITest(String title) { super(title); Container pane = getContentPane(); JLabel label =newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(label); // Create a new push button that may be used in addition to the [X] JButton button = newJButton("Exit"); // Add the button to the content of the window pane.add(button); } // ... }
Layout • Wir beobachten... • Der Text ist nicht mehr sichtbar, denn der Button liegt darüber! • Layout-Management • ist der Prozess, die Größe und Position von Komponenten zu bestimmen. • Das gewünschte Layout wird durch Zuordnung eines Layout-Objekts zum Container festgelegt:Container pane = getContentPane();pane.setLayout(newGridLayout(ROWS, COLS)); • Die Layout-Klasse implementiert das Interface LayoutManager.
Vordefinierte Layouts • GridLayout • Ordnet die Komponenten in einemrechteckigen Gitter an. • Die Reihenfolge des Einfügens derKomponenten bestimmt ihre Anordnung. • BorderLayout • Positioniert in 5 Regionen jeweilsmaximal eine Komponente • Die Regionen N, E, S und W werdenso klein wie möglich gehalten. Derrestliche Platz entfällt auf CENTER. • Zuordnung zu einer Region durch zusätzlichen Parameter beim Aufruf von Container.add:add(new Button("SOUTH"), BorderLayout.SOUTH); • Weitere Layouts: BoxLayout, FlowLayout, GridBagLayout, ... • Siehe die Java API Dokumentation und die Tutorials für mehr Informationen
GridLayout importjava.awt.Container; importjava.awt.GridLayout; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { publicGUITest(String title) { super(title); Container pane = getContentPane(); // Define a LayoutManager that places new elements properly // onto the pane. Here we use a grid with 3 rows and 1 column. pane.setLayout(newGridLayout(3, 1)); JLabel label = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(label); JButton button = newJButton("Exit"); pane.add(button); } ... }
Ereignisse (Events) • Jedesmal, wenn der Benutzer eine Taste drückt oder die Maus bewegt, tritt ein Ereignis ein. • Steuerelemente können Maus- und Tastatur-Ereignisse verarbeiten und neue Ereignisse erzeugen. • Beispiel: Befindet sich der Mauszeiger über einem Button und die Maustaste wird losgelassen, dann wird ein ActionEvent erzeugt. • Ereignisse werden durch Event-Objekte beschrieben • Die zugehörigen Klassen sind von java.awt.AWTEvent abgeleitet. (für GUI; allgemeiner: java.util.EventObject) • Der Typ gibt Auskunft über die Art des Ereignisses:ActionEvent: Benutzer klickt einen Button, drückt Return in einem Textfeld, wählt einen Menüeintrag, ...WindowEvent: Benutzer schließt Fenster, ...... • Die Attribute geben zusätzliche Informationen über das Ereignis, z.B. welcher Button gedrückt wurde.
Listener • Mehrere Listener können sich bei einer Ereignis-Quelle (eventsource) registrieren und werden von diesem Zeitpunkt an über Ereignisse eines bestimmten Typs informiert. • Programmieren eines Listeners: • Das Klasse des Empfänger-Objekts (eventlistener) muss das dem Ereignistypen entsprechende Interface implementieren. • Z.B. für ActionEvents das Interface ActionListener. • Die Klasse muss alle Methoden des Interface implementieren. • Der Klient registriert nun das Empfänger-Objekt bei der Ereignis-Quelle. Z.B. mittels addActionListener(Listener) event listener event object event source event listener event listener
Ereignisse importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { publicGUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); JLabel label = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(label); pane.add(newJButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newChangeButtonListener()); pane.add(button); } privateclassChangeButtonListenerimplementsActionListener { publicvoidactionPerformed(ActionEvent event) { System.out.println("Change Label Text was clicked"); } } // ... } Das ist eine verschachtelte Klasse (nestedclass). Später mehr dazu!
View-Updates • Die durch Komponenten dargestellten Informationen und ihr Aussehen können durch Aufrufen ihrer Methoden verändert werden. • JLabel-API (Auszug):
Ereignisse importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { private JLabelinfoLabel; publicGUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); infoLabel = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(infoLabel); pane.add(newJButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newChangeButtonListener()); pane.add(button); } privateclassChangeButtonListenerimplementsActionListener { publicvoidactionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button!Now [Exit] or [X]"); } } // ... }
Window-Events • Beim Klicken auf den Schließen-Button soll das Programm nun korrekt beendet werden. Registrieren eines WindowListeners • Interface WindowListener:
Adapter • Beim Verwenden von Interfaces müssen immer alle Methoden implementiert werden. Das ist oft unpraktisch, deshalb existieren Adapter. • WindowAdapter… • implementiert WindowListener • definiert alle Methoden mit leerem Rumpf und somit ohne Verhalten • ist eine abstrakte Klasse. • Der anwendungsspezifische Event-Handler wird nun von WindowAdapter abgeleitet und überschreibt nur die „interessanten“ Methoden.
WindowEvents importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; importjava.awt.event.WindowAdapter; importjava.awt.event.WindowEvent; publicclassGUITestextendsJFrame { privateJLabelinfoLabel; publicGUITest(String title) { super(title); // Now, also define that the [X] terminatestheprogramcorrectly addWindowListener(newMyWindowListener()); Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); infoLabel = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(infoLabel); // Create a new push button that may be used in addition to the [X] JButton button = newJButton("Exit"); // Define that the program should exit if you click the button button.addActionListener(newExitButtonListener()); pane.add(button);
Window Events // Create another button that changes the text of the Label button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newChangeButtonListener()); pane.add(button); } privateclassChangeButtonListenerimplementsActionListener { publicvoidactionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button!Now [Exit] or [X]"); } } // Exit the program when the window close button is clicked classMyWindowListenerextendsWindowAdapter { publicvoidwindowClosing(WindowEvent e) { System.exit(0); } } // Exit the program when the “Exit”-button is clicked classExitButtonListenerimplementsActionListener { publicvoidactionPerformed(ActionEvent event) { System.exit(0); } } public static void main(String args[]) { GUITesttheWindow = newGUITest("My first GUI Application"); theWindow.setSize(WIDTH, HEIGHT); theWindow.setVisible(true); } publicstaticfinalint WIDTH = 400; publicstaticfinalint HEIGHT = 300; }
Verschachtelte Klassen: Motivation • Die GUI Komponenten erwarten als “Listener” eine Instanz von XYZListener (z.B. ActionListener) • Wie können wir ein passendes Listener Objekt erstellen? • Option 1: Frame-Klasse implementiert das Listener Interface direkt • Option 2: Externe Klasse implementiert das Listener Interface
Option 1: Frame-Klasse implementiertdas Listener Interface direkt importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrameimplementsActionListener { publicGUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); JLabel label = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(label); pane.add(new JButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(this); pane.add(button); } publicvoidactionPerformed(ActionEvent event) { System.out.println("Change Label Text was clicked"); } // ... }
Option 1: Frame-Klasse implementiertdas Listener Interface direkt • Diese Möglichkeit funktioniert zwar, ist aber im Allgemeinen unbefriedigend • Was ist, wenn es mehr als einen Button gibt? • Klasse kann nicht zwei actionPerformed Methoden haben! • Es gibt nicht nur Buttons, sondern auch Labels, Menus, … • Frame-Klasse implementiert eine Menge an Interfaces, die nichts mit seiner eigentlichen Funktionalität zu tun haben • action-Methoden müssen public sein, obwohl es eigentlich Implementationsdetails sind, die nicht zur Schnittstelle von Frame gehören • Im Allgemeinen ist dies eine schlechte Lösung
Option 2: Externe Klasse implementiertdas Listener Interface importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { privateJLabelinfoLabel; publicGUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); infoLabel = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(infoLabel); pane.add(new JButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newChangeButtonListener()); pane.add(button); } } classChangeButtonListenerimplementsActionListener { publicvoidactionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button!Now [Exit] or [X]"); } }
Option 2: Externe Klasseimplementiert das Listener Interface • Hat nicht die konzeptuellen Nachteile von Option 1 • z.B. kein Problem, unterschiedliche Aktionen für unterschiedliche Buttons zu definieren • Das Interface des Frames wird nicht verunstaltet • Nachteile dieses Ansatzes • Führt zu sehr vielen Klassen • Übersichtlichkeit leidet • Geringere Kohäsion • Externe Listener-Klasse wird evtl. nur an genau einer Stelle benutzt • Aufwändig und lästig, für jeden Listener eine eigene Klasse anzulegen • Zugriff auf Instanzvariablen/Methoden/lokale Variablen der Frame Klasse aufwändig • Alles muss explizit im Konstruktor übergeben werden
Option 2: Externe Klasseimplementiert das Listener Interface importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { privateJLabelinfoLabel; publicGUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); infoLabel = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(infoLabel); pane.add(newJButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newChangeButtonListener(infoLabel)); pane.add(button); } } classChangeButtonListenerimplementsActionListener { privateJLabelinfoLabel; publicChangeButtonListener(JLabel l) { infoLabel = l; } publicvoidactionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button!Now [Exit] or [X]"); } }
Verschachtelte Klassen • Beide Möglichkeiten (Option 1 + Option 2) sind nicht zufriedenstellend • Dies war die Hauptmotivation für Sun, mit Java 1.1 die verschachtelten Klassen (nestedclasses) einzuführen • Verschachtelte Klassen sind Klassen innerhalb von Klassen • Werden deklariert wie ganz normale Klassen, außer dass sie innerhalb anderer Klassen stehen • Einige Zugriffsregeln sind anders • Verschachtelte Klassen haben Zugriff auf Instanzvariablen/Methoden der äußeren Klasse
Verschachtelte Klassen importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { privateJLabelinfoLabel; publicGUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(infoLabel); pane.add(newJButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newChangeButtonListener()); pane.add(button); } privateclassChangeButtonListenerimplementsActionListener { publicvoidactionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button!Now [Exit] or [X]"); } } // ... } • Nested Class kann “private” sein • Klasse nur innerhalb der umschließenden Klasse sichtbar • hat Zugriff auf private Instanzvariable der umschließenden Klasse
Verschachtelte Klassen • Jede verschachtelte Klasse benötigt bei der Instanziierung eine Referenz auf eine Instanz der umschließenden Klasse • Bei Instanziierung innerhalb der umschließenden Klasse wird diese Referenz implizit übergeben • Außerhalb einer umschließenden Klasse als enclInstance.newMyInnerClass() • Der Typ einer nested Class N innerhalb C ist “C.N” • Innerhalb von C reicht die Typdeklaration N (implizites Scoping) • Daumenregel: Wenn der Name einer nestedclass außerhalb der umschließenden Klasse benötigt wird, hat man etwas falsch gemacht. • Verschachtelte Klassen können auch als “static” deklariert werden • Semantik analog zu staticmethod: Kein Zugriff auf Instanzvariablen der äußeren Klasse, sondern nur auf static Methoden/Variablen
Anonyme verschachtelte Klassen importjava.awt.Container; importjava.awt.GridLayout; importjava.awt.event.ActionEvent; importjava.awt.event.ActionListener; importjavax.swing.JButton; importjavax.swing.JFrame; importjavax.swing.JLabel; publicclassGUITestextendsJFrame { publicGUITest(String title) { super(title); finalJLabelinfoLabel; // now a local variable Container pane = getContentPane(); pane.setLayout(newGridLayout(3, 1)); infoLabel = newJLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(infoLabel); pane.add(newJButton("Exit")); // Create another button that changes the text of the Label JButton button = newJButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(newActionListener() { public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button!Now [Exit] or [X]"); } }); pane.add(button); } }
Anonyme verschachtelte Klassen • Man kann innerhalb von Methoden anonyme Klassen anlegen • Diese haben keinen Namen • Sie werden nur über Supertypen angesprochen • Syntax: newSuperClassName(args) { … } odernew InterfaceName() { … } • Erzeugt implizit eine Subklasse der angegebenen Superklasse bzw. eine Klasse, die das angegebene Interface implementiert • Gleichzeitig wird eine Instanz der anonymen Klasse erzeugt • Diese Klasse kann über den angegebenen Superklassen/Interface Namen benutzt werden • Sogar lokale Variablen der Methode können in der Implementierung verwendet werden, sofern sie als “final” deklariert sind
Anonyme verschachtelte Klassen • Vorteil gegenüber “normalen” verschachtelten Klassen • Wenn die Klasse nur an einem einzigen Ort benötigt wird, wird kein Name “verschwendet” • Definition der Klasse “on the fly” – dort wo sie benötigt wird • Einfacher Zugriff auf lokale Variablen / Methodenparameter • Anonyme Klassen kommen dem “lambda” aus Scheme recht nahe • Mit “lambda” können in Scheme Prozeduren “on the fly” definiert werden
Kompilierung von verschachtelten Klassen • Wenn eine verschachtelte Klasse kompiliert wird, bekommt man für jede verschachtelte Klasse ein eigenes .classfile mit dem Namen “OuterClass$InnerClass.class” • Wenn eine anonyme Klasse kompiliert wird, heißt das File “OuterClass$n.class”, wobei n eine ganze Zahl ist • Intern (auf JVM Ebene) gibt es keine verschachtelten Klassen • Der Compiler transformiert intern den Code • Für jede verschachtelte Klasse wird eine Top-Level Klasse angelegt, die alle benötigten Argumente im Konstruktor übergeben bekommt
GUI und Software Design • GUIs können sehr aufwändig und kompliziert sein • Die eigentliche Anwendung ebenfalls • Wie strukturiert man eine Anwendung mit komplexer GUI? • Regel Nr.1: Trennen Sie die Anwendungslogik von der Präsentationslogik
GUI Antipatterns • Sie machen etwas falsch, wenn… • Sie in ihrer GUI-Klasse auf eine Datenbank zugreifen • Ihre GUI-Klasse mehr als 50 KB groß ist • in der GUI-Klasse Geschäftsprozesse implementiert werden • Ihre Anwendungsdaten in GUI-Klassen gespeichert sind • … • Besser: Schichtenarchitektur • z.B. sog. “Model-View-Controller Architektur” • trennt Daten/Anwendungslogik von Interaktion mit Benutzer • Daten/Anwendungslogik hängt nicht von GUI ab • einfach, mehrere Views zu unterstützen • Austausch von Views einfach
A B C 45 35 15 Model-View-Controller Controller View Event senden BehandeltNutzer- eingaben Anzeige controller updates Zugriff Aktualisieren Hinweisauf Änderung Model Anwendungsdatenund Verhalten
MVC – Das Modell • Das Modell enthält die Daten • Hat Methoden, um auf Daten zuzugreifen und sie zu ändern • Benachrichtigt Listener/Observer, wenn sich die Daten ändern • Kann (Teile) der Geschäftsprozesse implementieren
MVC – Der Controller • Verarbeitet die Eingaben des Benutzers • zum Beispiel Validierung, Interpretation jeglicher Art, … • Häufig zuständig für die Benutzerführung der GUI • Kommuniziert diese Eingaben zum Modell • Kann präsentationsnahe Teile der Anwendungslogik enthalten • z.B. Interaktionsfluss mit dem Benutzer • Scharfe Trennung vom Modell ist oft schwierig • Kann (muss aber nicht) spezifisch für eine feste GUI sein • Controller hat oder hat nicht eine Instanzvariable die auf die GUI zeigt • evtl. entkoppelt über Interface • Bessere Wiederverwendbarkeit im letzteren Fall • Schichtenstruktur
MVC – Die View • Die View stellt eine visuelle Repräsentation des Modells dar • Es kann beliebig viele Views auf denselben Daten geben • z.B. mehrere Instanzen derselben View • Aber auch völlig unterschiedliche Views • Zum Beispiel können die Finanzen einer Firma als Tabelle oder als Graph dargestellt werden • Registriert sich beim Modell als Listener, zeichnet sich neu, wenn Modell sich ändert.
Internationalization (I18N) • Internationalisierung: Bereitstellung von Inhalten in mehreren Sprachen • Problematisch für GUIs: was muss sich ändern? • Beispiel - Menüeintrag: • Text • [optionales] Icon • Kürzel (hier: ALT+G) • Tool tip (“Generate…”) • Englische Fassung:
Internationalization (I18N) • Java bietet Basiselemente für Internationalization • Die GUI-Anpassung muss aber vom Programmierer erfolgen • Alternative: Nutzung des translator Package • Siehe die Beispiele auf den folgenden Folien • Grundlegender Ansatz: • Alle sprachspezifischen Elemente kommen in eine Datei • Eine Datei pro unterstützter Sprache • Die Zeilen der Datei sind im Format key=Wert • key: ein String, der das Element beschreibt • Wert: der auszugebende Wert für den key • Anstelle “echter” Texte wird im Code nur der key genutzt
Sprachressourcen • Alle Ressourcen kommen in die sprachspezifische Datei • Diese Dateien haben alle den gleichen Basisnamen • Zum Beispiel “msg” • Die Dateierweiterung hat das Format land_SPRACHE • Deutsche Ressourcen stehen also in “msg.de_DE” • Die US-Englische Fassung steht in “msg.en_US” • … • Beispiel für einen Inhalt: msg.en_US msg.de_DE • hi=Hello! • query=How are you today? • hi=Hallo! • query=Wiegehtes Dir?
translatorPackage: Translator • Ziel: Inhaltsübersetzung für eine Zielsprache • Nutzt java.util.Localefür die Sprachbestimmung • Vordefinierte Locales: Locale.GERMANY, Locale.US, … • Die Zielausgabe hängt ab von bis zu drei Attributen: • Sprache: “de”, “en”, “es”, … • Land: “de”, “us”, “uk”, … • Variante: spezifischer Code für Browser etc., z.B. „WIN” • Anlegen einer neuen Instanz von Translator • Parameter: Basisname der Ressourcen und Ziel-Locale: TranslatormyTrans = newTranslator(“msg”, Locale.GERMANY);