Technische Universität Darmstadt Telecooperation/RBG Grundlagen der Informatik 1 Thema 21: GUI und verschachtelte Klassen Prof. Dr. Max Mühlhäuser Dr. Guido Rößling Copyrighted material; for TUD student use only Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 2 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Komponenten • Einige oft verwendete Komponenten und für welche Interaktionsaufgaben sie verwendet werden • Anzeigen von Text und Symbolen – JLabel • Auslösen von Aktionen – JButton – JMenu – JMenuItem Grundlagen der Informatik I: T21 3 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Komponenten • Eingabe von Text – JTextField • Auswahl aus einer festen Menge von Optionen – JCheckBox gegenseitig ausschließend: JRadioButton • Auswahl aus einer variablen Menge – JList JComboBox Button oder TextField + ausklappbare Listbox Grundlagen der Informatik I: T21 4 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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. Grundlagen der Informatik I: T21 5 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Anatomie einer GUI-Applikation GUI-Framework Netz von kooperierenden Objekten mit klar festgelegten Zuständigkeiten: Container • • • • • Komponenten Ereignisse Anwendungslogik Methodenaufrufe auf Komponente Listener Zustand: wert=3 Komponenten Container Ereignisse Listener Layout – Legt die Anordnung der Komponenten fest • Look & Feel – Legt das Aussehen der Komponenten fest • getrennt: Anwendungslogik Grundlagen der Informatik I: T21 6 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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. Faden Programm • Nebenläufige Programme – Programme können mehrere Fäden besitzen. – Mehrere Programmteile können so quasi gleichzeitig ablaufen. Fäden Programm Grundlagen der Informatik I: T21 7 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 (lightweight processes) 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 Grundlagen der Informatik I: T21 8 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Programmiermodell Anwendung GUI-Framework (SWING) main main Komponente einfügen AWT-EventQueue AWT-EventQueue Ereignis: Komponente zeichnen AWT-EventQueue Ereignis: Benutzereingabe Grundlagen der Informatik I: T21 9 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Fenster import javax.swing.JFrame; public class GUITest extends JFrame { // The width of the window in pixels public static final int WIDTH = 400; // The height of the window in pixels public static final int HEIGHT = 300; // Constructs a new window with a given title public GUITest(String title) { super(title); } // Start test application. Creates a new window and displays it public static void main(String args[]) { // Construct a new window. It is initially invisible GUITest theWindow = new GUITest("My first GUI Application"); // Set width and height of the window theWindow.setSize(WIDTH, HEIGHT); // Open the window theWindow.setVisible(true); } } System.out.println("Exiting main..."); Grundlagen der Informatik I: T21 10 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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. Grundlagen der Informatik I: T21 11 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Container • Container und Komponenten JFrame JPanel – JFrame: „top level container“ • benutzt Fenster vom Betriebssystem JLabel – 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 aus einer Hierarchie von Containern und Komponenten – (Darstellung vereinfacht. Die Container zwischen JFrame und JPanel werden hier nicht weiter betrachtet) Grundlagen der Informatik I: T21 JFrame ... JPanel JLabel 12 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Komponenten • Hinzufügen von Komponenten JFrame – Der JFrame erzeugt automatisch ein JPanel, das „contentPane“. – Darin kann die Anwendung neue contentPane Komponenten einfügen. – Die Komponenten werden eingefügt, während das Fenster noch unsichtbar ist, also zwischen dem Erstellen des Fenster-Objekts mit new und der Anzeige mit setVisible(true). ... JPanel JLabel • JLabels – stellen Text oder/und Symbole dar – sind passive Komponenten, erlauben keine Interaktion Grundlagen der Informatik I: T21 13 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Labels import java.awt.Container; import javax.swing.JFrame; import javax.swing.JLabel; public class GUITest extends JFrame { public GUITest(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 = new JLabel( "Press the [X] in the top right corner to exit"); } } // Add the label to the content of the window pane.add(label); // ... Grundlagen der Informatik I: T21 14 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Buttons import java.awt.Container; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; public class GUITest extends JFrame { public GUITest(String title) { super(title); Container pane = getContentPane(); JLabel label = new JLabel( "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 = new JButton("Exit"); } } // Add the button to the content of the window pane.add(button); // ... Grundlagen der Informatik I: T21 15 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 LayoutObjekts zum Container festgelegt: Container pane = getContentPane(); pane.setLayout(new GridLayout(ROWS, COLS)); – Die Layout-Klasse implementiert das Interface LayoutManager. Grundlagen der Informatik I: T21 16 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Vordefinierte Layouts • GridLayout – Ordnet die Komponenten in einem rechteckigen Gitter an. – Die Reihenfolge des Einfügens der Komponenten bestimmt ihre Anordnung. • BorderLayout – Positioniert in 5 Regionen jeweils maximal eine Komponente – Die Regionen N, E, S und W werden so klein wie möglich gehalten. Der restliche 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 Grundlagen der Informatik I: T21 17 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © GridLayout import java.awt.Container; import java.awt.GridLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; public class GUITest extends JFrame { public GUITest(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(new GridLayout(3, 1)); JLabel label = new JLabel( "Press the [Exit] or [X] in the top right corner to exit"); pane.add(label); JButton button = new JButton("Exit"); pane.add(button); } } ... Grundlagen der Informatik I: T21 18 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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. Grundlagen der Informatik I: T21 19 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Listener • Mehrere Listener können sich bei einer Ereignis-Quelle (event source) registrieren und werden von diesem Zeitpunkt an über Ereignisse eines bestimmten Typs informiert. event source event object event listener event listener event listener • Programmieren eines Listeners: – Das Klasse des Empfänger-Objekts (event listener) 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) Grundlagen der Informatik I: T21 20 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ereignisse import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class GUITest extends JFrame { public GUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); JLabel label = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(new ChangeButtonListener()); pane.add(button); } private class ChangeButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Change Label Text was clicked"); } } // ... Das ist eine verschachtelte Klasse (nested class). Später mehr dazu! } Grundlagen der Informatik I: T21 21 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © View-Updates • Die durch Komponenten dargestellten Informationen und ihr Aussehen können durch Aufrufen ihrer Methoden verändert werden. • JLabel-API (Auszug): void setText(String) String getText() Setzen oder auslesen des dargestellten Texts void setHorizontalAlignment(int) int getHorizontalAlignment() Textausrichtung: LEFT, CENTER oder RIGHT void setVerticalAlignment(int) int getVerticalAlignment() Textausrichtung: TOP, CENTER oder BOTTOM Grundlagen der Informatik I: T21 22 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ereignisse import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class GUITest extends JFrame { private JLabel infoLabel; public GUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(new ChangeButtonListener()); pane.add(button); private class ChangeButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button! Now [Exit] or [X]"); } } // ... Grundlagen der Informatik I: T21 23 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Window-Events • Beim Klicken auf den Schließen-Button soll das Programm nun korrekt beendet werden. Registrieren eines WindowListeners • Interface WindowListener: void windowActivated(WindowEvent e) Window wurde aktiviert void windowClosed(WindowEvent e) Fenster wurde nach dispose() geschlossen void windowClosing(WindowEvent e) Benutzer hat auf „Schließen“ geklickt void windowDeactivated(WindowEvent e) Fenster wurde deaktiviert void windowDeiconified(WindowEvent e) Fenster wurde de-minimiert void windowIconified(WindowEvent e) Fenster wurde minimiert void windowOpened(WindowEvent e) Fenster wurde gerade geöffnet Grundlagen der Informatik I: T21 24 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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. Grundlagen der Informatik I: T21 25 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © WindowEvents import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class GUITest extends JFrame { private JLabel infoLabel; public GUITest(String title) { super(title); // Now, also define that the [X] terminates the program correctly addWindowListener(new MyWindowListener()); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = new JLabel( "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 = new JButton("Exit"); // Define that the program should exit if you click the button button.addActionListener(new ExitButtonListener()); pane.add(button); Grundlagen der Informatik I: T21 26 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Window Events // Create another button that changes the text of the Label button = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(new ChangeButtonListener()); pane.add(button); } private class ChangeButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button! Now [Exit] or [X]"); } } // Exit the program when the window close button is clicked class MyWindowListener extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } // Exit the program when the “Exit”-button is clicked class ExitButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.exit(0); } } } public static void main(String args[]) { GUITest theWindow = new GUITest("My first GUI Application"); theWindow.setSize(WIDTH, HEIGHT); theWindow.setVisible(true); } public static final int WIDTH = 400; public static final int HEIGHT = 300; Grundlagen der Informatik I: T21 27 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 28 Option 1: Frame-Klasse implementiert das Listener Interface direkt Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; public class GUITest extends JFrame implements ActionListener { public GUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); JLabel label = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(this); pane.add(button); } public void actionPerformed(ActionEvent event) { System.out.println("Change Label Text was clicked"); } // ... } Grundlagen der Informatik I: T21 29 Option 1: Frame-Klasse implementiert das Listener Interface direkt Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © • 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 Grundlagen der Informatik I: T21 30 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Option 2: Externe Klasse implementiert das Listener Interface import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class GUITest extends JFrame { private JLabel infoLabel; public GUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed } } button.addActionListener(new ChangeButtonListener()); pane.add(button); class ChangeButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button! Now [Exit] or [X]"); } } Grundlagen der Informatik I: T21 31 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Option 2: Externe Klasse implementiert 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 Grundlagen der Informatik I: T21 32 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Option 2: Externe Klasse implementiert das Listener Interface import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class GUITest extends JFrame { private JLabel infoLabel; public GUITest(String title) { super(title); Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed } } button.addActionListener(new ChangeButtonListener(infoLabel)); pane.add(button); class ChangeButtonListener implements ActionListener { private JLabel infoLabel; public ChangeButtonListener(JLabel l) { infoLabel = l; } public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button! Now [Exit] or [X]"); } } Grundlagen der Informatik I: T21 33 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 (nested classes) 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 Grundlagen der Informatik I: T21 34 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Verschachtelte Klassen import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class GUITest extends JFrame { private JLabel infoLabel; public GUITest(String title) { super(title); - Nested Class kann “private” sein - Klasse nur innerhalb der umschließenden Klasse sichtbar - hat Zugriff auf private Instanzvariable der umschließenden Klasse Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed } button.addActionListener(new ChangeButtonListener()); pane.add(button); private class ChangeButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button! Now [Exit] or [X]"); } } // } ... Grundlagen der Informatik I: T21 35 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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.new MyInnerClass() – 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 nested class 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 static method: Kein Zugriff auf Instanzvariablen der äußeren Klasse, sondern nur auf static Methoden/Variablen Grundlagen der Informatik I: T21 36 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Anonyme verschachtelte Klassen import import import import import import import java.awt.Container; java.awt.GridLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; public class GUITest extends JFrame { public GUITest(String title) { super(title); final JLabel infoLabel; // now a local variable Container pane = getContentPane(); pane.setLayout(new GridLayout(3, 1)); infoLabel = new JLabel( "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 = new JButton("Change Label Text"); // Now, define what should happen if the button is pressed button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { infoLabel.setText("You clicked the button! Now [Exit] or [X]"); } }); pane.add(button); } } Grundlagen der Informatik I: T21 37 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Anonyme verschachtelte Klassen • Man kann innerhalb von Methoden anonyme Klassen anlegen – Diese haben keinen Namen – Sie werden nur über Supertypen angesprochen – Syntax: new SuperClassName(args) { … } oder new 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 Grundlagen der Informatik I: T21 38 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 39 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Kompilierung von verschachtelten Klassen • Wenn eine verschachtelte Klasse kompiliert wird, bekommt man für jede verschachtelte Klasse ein eigenes .class file 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 Grundlagen der Informatik I: T21 40 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 41 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 42 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Model-View-Controller Event senden Behandelt Nutzereingaben Controller controller updates View Anzeige Model A B C 45 35 15 Anwendungsdaten und Verhalten Grundlagen der Informatik I: T21 43 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 44 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 45 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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. Grundlagen der Informatik I: T21 46 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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: Grundlagen der Informatik I: T21 47 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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 Grundlagen der Informatik I: T21 48 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © 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! hi=Hallo! query=How are you today? query=Wie geht es Dir? Grundlagen der Informatik I: T21 49 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © translator Package: Translator • Ziel: Inhaltsübersetzung für eine Zielsprache • Nutzt java.util.Locale fü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: Translator myTrans = new Translator(“msg”, Locale.GERMANY); Grundlagen der Informatik I: T21 50 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © translator Package: Translator • Übersetzen einer Nachricht über String myTrans.translateMessage(key) • Beispiel: – myTrans.translateMessage("hi") Hallo! • Manchmal muss die Ausgabe parametrisiert werden – Wie begrüßt man eine Person? “Hallo, Herr Schmidt!” Grundlagen der Informatik I: T21 51 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Übersetzen parametrisierter Texte • Das Package unterstützt Parameter in {}: – Angenommen, wir haben diese Definition in unserer Datei: personalized=Hallo, {0}! – Nun können wir einen Parameter übergeben…: myTrans.translateMessage("personalized", "Herr Schmidt"); Hallo, Herr Schmidt! • Parameter werden als Zahl ab 0 in {} gesetzt • Wenn mehr als ein Parameter erforderlich ist, … – kommen diese in ein Array: translateMessage(key, Object[]) – übergibt man sie als Strings: translateMessage(key, String…) Grundlagen der Informatik I: T21 52 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Internationalisierte GUI-Anwendung • Wir werden nun eine kleine GUI-Anwendung bauen – Ein JFrame dient als Hauptfenster – Mit einem JMenuBar • Mit einem “Datei”-Menü – Das hat einen “Exit”-Eintrag – Zwei umrahmte JPanel: • Eines mit einem JLabel und einem JButton • Das zweite hat nur einen “Beenden” JButton mit Icon • Jeder Klick auf den Button erhöht einen Zähler • Verlässt man die Anwendung über den Beenden-Button, wird die Anzahl Klicks angezeigt • Keine Sorge, das ist einfacher als es wirkt… Grundlagen der Informatik I: T21 53 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © I18N GUI Demo – Code I import translator.TranslatableGUIElement; // for GUI elements import translator.Translator; // for I18N of texts etc. // Swing & Event imports skipped – use Eclipse auto-completion! public class I18NDemo { public int nrTimes = 0; // how often was the button clicked? /** * create the I18NDemo instance * @param targetLocale the Locale used for the output */ public I18NDemo(Locale targetLocale) { // create the translator (resource file; target locale) Translator translator = new Translator("guiI18N", targetLocale); buildGUI(translator); // create the GUI } Grundlagen der Informatik I: T21 54 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © I18N GUI Demo – Code II /** * builds the GUI elements with full I18N support * @param translator the Translator instance to be used */ public void buildGUI(final Translator translator) { // retrieve the GUI element builder TranslatableGUIElement guiBuilder = translator.getGenerator(); // create the window itself with an I18N title JFrame aFrame = guiBuilder.generateJFrame("guiDemo"); aFrame.getContentPane().setLayout(new BorderLayout()); // create a JMenuBar JMenuBar menuBar = new JMenuBar(); Grundlagen der Informatik I: T21 55 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © I18N GUI Demo – Code III // generate the JMenu for this JMenu menu = guiBuilder.generateJMenu("fileMenu"); // generate a menu item with (key, useIconIfExists) JMenuItem exitItem = guiBuilder.generateJMenuItem("exitItem", true); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); // exit }}); // add the item to the JMenu menu.add(exitItem); // add the menu to the menu bar and add this to the JFrame menuBar.add(menu); aFrame.setJMenuBar(menuBar); Grundlagen der Informatik I: T21 56 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © I18N GUI Demo – Code IV JPanel infoPanel = guiBuilder.generateBorderedJPanel("infoPanel"); aFrame.getContentPane().add(infoPanel, BorderLayout.CENTER); // translatable JLabel JLabel label = guiBuilder.generateJLabel("clickMe"); infoPanel.add(label); // add the info button AbstractButton info = guiBuilder.generateJButton("clickButton"); info.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.err.println(translator.translateMessage("pressedN", String.valueOf(++nrTimes))); // show nr of button press } }); infoPanel.add(info); // add the info panel Grundlagen der Informatik I: T21 57 I18N GUI Demo – Code V Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © JPanel exitPanel = guiBuilder.generateBorderedJPanel("exitPanel"); aFrame.getContentPane().add(exitPanel, BorderLayout.SOUTH); AbstractButton exit = guiBuilder.generateJButton("exitButton"); // exit button exit.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.err.println(translator.translateMessage("bye", String.valueOf(nrTimes)); System.exit(0); // show good bye & # of pressed, exit }}); exitPanel.add(exit); aFrame.pack(); aFrame.setVisible(true); } public static void main(String[] args) { I18NDemo demo = new I18NDemo(Locale.GERMANY); } } Grundlagen der Informatik I: T21 58 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ergebnis der GUI-Demo • Hier ist das Ausgabefenster: JFrame JMenuItem “Verlassen” mit Icon, Kürzel V, und Tooltip JMenuBar mit JMenu „Datei“, Kürzel D JLabel JButton, Kürzel Z Tooltip des Menüeintrags Umrahmtes JPanel JButton, Kürzel B, mit Icon Grundlagen der Informatik I: T21 59 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ergebnis der GUI-Demo • Und hier die englische Fassung: JFrame JMenuItem “Exit” mit Icon, Kürzel X, und Tooltip JMenuBar mit JMenu „File“, Kürzel F JLabel JButton, Kürzel I Tooltip des Menüeintrags Umrahmtes JPanel JButton, Kürzel X, mit Icon Um dieses Fenster zu erhalten, ändert man eine Zeile: I18NDemo demo = new I18NDemo(Locale.US); Grundlagen der Informatik I: T21 60 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Abschließende Kommentare • Sie haben es vielleicht nicht bemerkt… – Beide Buttons, das Menü und der Menüeintrag haben ein Kürzel (mnemonic) – Alle GUI-Elemente haben einen tool tip text • Aber die Texte etc. werden im Code nicht angegeben! – Weil sie alle aus der Ressource-Datei stammen – Diese wird auf den folgenden Folien gezeigt • Spielen Sie ein wenig mit der API – Sie finden Sie auf der Webseite mit Zusatzinfos – Der Source Code kann dort auch heruntergeladen werden • Man kann die Inhalte auch “on the fly” übersetzen – Einfach aufrufen translator.setTranslatorLocale(newLocale) – Kann etwa über einen Menüeintrag umgeschaltet werden Grundlagen der Informatik I: T21 61 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Englische Ressourcedatei zur GUI Demo hi=Hello! personalized=Hello, {0}! multi=One {0} two {1} three {2} parameters noSuchKeyException=There is no ressource for element {0} exitPanel=Exit Panel exitButton.iconName=quit.gif exitButton.label=Exit exitButton.mnemonic=x exitButton.toolTipText=Click this button to end the demo clickButton.iconName= clickButton.label=Increment counter clickButton.mnemonic=i clickButton.toolTipText=Click here to increment the counter infoPanel=Info Area clickMe=Click here: fileMenu.label=File fileMenu.mnemonic=f fileMenu.toolTipText=Contains the Exit item exitItem.iconName=quit.gif exitItem.label=Exit exitItem.mnemonic=x exitItem.toolTipText=Exits the application pressedTimes=The increment button was pressed {0} times. bye=Good bye - and thank you for pressing the button {0} times. guiDemo=Internationalization Demo Grundlagen der Informatik I: T21 62 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation Deutsche Ressourcedatei zur GUI Demo © hi=Hallo! personalized=Hallo, {0}! multi=Eins {0} zwei {1} drei {2} Parameter noSuchKeyException=Es gibt keine Ressource f\u00fcr Eintrag {0} exitPanel=Beenden exitButton.iconName=quit.gif exitButton.label=Beenden exitButton.mnemonic=b exitButton.toolTipText=Dr\u00fccken Sie diesen Knopf zum Beenden clickButton.iconName= clickButton.label=Z\u00e4hler inkrementieren clickButton.mnemonic=z clickButton.toolTipText=Klicken Sie hier, um den Z\u00fchler zu inkrementieren infoPanel=Info-Bereich clickMe=Bitte hier klicken: fileMenu.label=Datei fileMenu.mnemonic=d fileMenu.toolTipText=Enth\u0fe4lt nur den Eintrag 'Verlassen' exitItem.iconName=quit.gif exitItem.label=Verlassen exitItem.mnemonic=v exitItem.toolTipText=Beendet die Anwendung pressedTimes=Der Inkrementieren-Knopf wurde {0} Mal gedr\u00fcckt. bye=Auf Wiedersehen - und Danke, dass Sie den Knopf {0} Mal gedr\u00fcckt haben. guiDemo=Demo zur Internationalisierung Grundlagen der Informatik I: T21 63