1. Konventionelle Ein-/Ausgabebetonte Programmierung 1.1 Realisierung grafischer Benutzungsoberflächen – Beispiel Java AWT und Swing Grundlagen der 2D-Computergrafik – Beispiel Java-Grafikprogrammierung, Java 2D 1.2 Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 36 Ereignisgesteuerter Programmablauf • Definition 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 Benutzerinteraktionen. • Beispiele für Benutzerinteraktions-Ereignisse: – – – – – – – – 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 Ludwig-Maximilians-Universität München Prof. Hußmann Seite 1 Medientechnik – 1 - 37 Ereignis-Klassen • Klassen von Ereignissen 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 Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 38 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) { ExampleFrame f = new ExampleFrame(); } } Ludwig-Maximilians-Universität München Prof. Hußmann Seite 2 Medientechnik – 1 - 39 Ereignis-Delegation (1) Laufzeit-System betätigt Schliessknopf Benutzer • 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. Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 40 Ereignis-Delegation (2) Laufzeit-System betätigt Schliessknopf Benutzer f: Frame Quelle vom System neu erzeugt: e: WindowEvent registriert bei Delegation: Aufruf einer Methode von l mit Argument e l: WindowListener Ludwig-Maximilians-Universität München Prof. Hußmann Seite 3 Medientechnik – 1 - 41 Registrierung für Listener • In java.awt.Frame (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"-Mechanismus – Window-Listener ist eine "Observer"-Schnittstelle Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 42 java.awt.event.WindowListener 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" (keine Operationen) Ludwig-Maximilians-Universität München Prof. Hußmann Seite 4 Medientechnik – 1 - 43 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(); ... } Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 44 java.awt.event.ActionEvent, ActionListener public class ActionEvent extends AWTEvent { ... // Konstruktor, wird vom System 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); } Ludwig-Maximilians-Universität München Prof. Hußmann Seite 5 Medientechnik – 1 - 45 WindowListener für Ereignis "Schließen" import java.awt.*; import java.awt.event.*; class WindowCloser implements WindowListener { public public public public public public void void void void void void windowClosed (WindowEvent ev) {} windowOpened (WindowEvent ev) {} windowIconified (WindowEvent ev) {} windowDeiconified (WindowEvent ev) {} windowActivated (WindowEvent ev) {} windowDeactivated (WindowEvent ev) {} public void windowClosing(WindowEvent event) { System.exit(0); } } Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 46 Hauptprogramm für schließbares Fenster import java.awt.*; import java.awt.event.*; class WindowCloser implements WindowListener { ... siehe letzte 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();} } Ludwig-Maximilians-Universität München Prof. Hußmann Seite 6 Medientechnik – 1 - 47 java.awt.event.WindowAdapter public abstract class WindowAdapter implements WindowListener { public public public public public public public void void void void void void void windowClosed (WindowEvent ev) {} windowOpened (WindowEvent ev) {} windowIconified (WindowEvent ev) {} windowDeiconified (WindowEvent ev) {} windowActivated (WindowEvent ev) {} windowDeactivated (WindowEvent ev) {} windowClosing (WindowEvent ev) {} } Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 48 Vereinfachung 1: WindowAdapter benutzen 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();} } Ludwig-Maximilians-Universität München Prof. Hußmann Seite 7 Medientechnik – 1 - 49 Schließbares Fenster: Klassenstruktur <<interface>> WindowListener Window windowClosing (e: WindowEvent) addWindowListener (l: WindowListener) <<use>> WindowEvent Frame setSize setTitle setVisible WindowAdapter WindowCloser registriert bei Ludwig-Maximilians-Universität München ExampleFrame Prof. Hußmann Medientechnik – 1 - 50 Vereinfachung 2: Innere Klasse benutzen import java.awt.*; import java.awt.event.*; class ExampleFrame extends Frame { 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();}} Ludwig-Maximilians-Universität München Prof. Hußmann Seite 8 Medientechnik – 1 - 51 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(); } } Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 52 Zähler-Beispiel: Entwurf der Bedienelemente JPanel buttonPanel JButton countButton Ludwig-Maximilians-Universität München JButton resetButton Prof. Hußmann Seite 9 JButton exitButton Medientechnik – 1 - 53 Die Sicht (View): Bedienelemente class class CounterFrame CounterFrame extends extends JFrame JFrame {{ JPanel JPanel valuePanel valuePanel == new new JPanel(); JPanel(); JTextField JTextField valueDisplay valueDisplay == new new JTextField(10); JTextField(10); JPanel JPanel buttonPanel buttonPanel == new new JPanel(); JPanel(); JButton countButton = new JButton("Count"); JButton resetButton = new JButton("Reset"); JButton exitButton = new JButton("Exit"); }} public public CounterFrame CounterFrame (Counter (Counter c) c) {{ setTitle("SwingCounter"); setTitle("SwingCounter"); valuePanel.add(new valuePanel.add(new JLabel("Counter JLabel("Counter value")); value")); valuePanel.add(valueDisplay); valuePanel.add(valueDisplay); valueDisplay.setEditable(false); valueDisplay.setEditable(false); getContentPane().add(valuePanel); getContentPane().add(valuePanel); buttonPanel.add(countButton); buttonPanel.add(resetButton); buttonPanel.add(exitButton); getContentPane().add(buttonPanel); getContentPane().add(buttonPanel); pack(); pack(); setVisible(true); setVisible(true); }} Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 54 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 Ludwig-Maximilians-Universität München Prof. Hußmann Seite 10 Medientechnik – 1 - 55 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 Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 56 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) Ludwig-Maximilians-Universität München Prof. Hußmann Seite 11 Medientechnik – 1 - 57 Grid-Layout • Grundprinzip: – Anordnung nach Zeilen und Spalten • Parameter bei Konstruktor: – Abstände, Anzahl Zeilen, Anzahl Spalten • Constraints bei add: – Zeilen- und Spaltenindex als int-Zahlen 1,1 1,2 1,3 2,1 2,2 2,3 Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 58 Die Sicht (View): Alle sichtbaren Elemente class class CounterFrame CounterFrame extends extends JFrame JFrame {{ JPanel JPanel valuePanel valuePanel == new new JPanel(); JPanel(); JTextField JTextField valueDisplay valueDisplay == new new JTextField(10); JTextField(10); JPanel buttonPanel = new JPanel(); JPanel buttonPanel = new JPanel(); JButton JButton countButton countButton == new new JButton("Count"); JButton("Count"); JButton JButton resetButton resetButton == new new JButton("Reset"); JButton("Reset"); JButton JButton exitButton exitButton == new new JButton("Exit"); JButton("Exit"); }} public public CounterFrame CounterFrame (Counter (Counter c) c) {{ setTitle("SwingCounter"); setTitle("SwingCounter"); valuePanel.add(new valuePanel.add(new JLabel("Counter JLabel("Counter value")); value")); valuePanel.add(valueDisplay); valuePanel.add(valueDisplay); valueDisplay.setEditable(false); valueDisplay.setEditable(false); getContentPane().add(valuePanel, getContentPane().add(valuePanel, BorderLayout.NORTH); BorderLayout.NORTH); buttonPanel.add(countButton); buttonPanel.add(countButton); buttonPanel.add(resetButton); buttonPanel.add(resetButton); buttonPanel.add(exitButton); buttonPanel.add(exitButton); getContentPane().add(buttonPanel, getContentPane().add(buttonPanel, BorderLayout.SOUTH); BorderLayout.SOUTH); pack(); pack(); setVisible(true); setVisible(true); }} Ludwig-Maximilians-Universität München Prof. Hußmann Seite 12 Medientechnik – 1 - 59 Model-View-Controller-Architektur View Controller bc: ButtonController cf: CounterFrame <<beobachtet>> <<steuert>> Counter –k + count() + reset() + getValue() c: Counter k=7 Model Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 60 Zähler-Beispiel: Anbindung Model/View class class CounterFrame CounterFrame extends extends JFrame JFrame implements implements Observer Observer {{ ... ... JTextField JTextField valueDisplay valueDisplay == new new JTextField(10); JTextField(10); ... ... public public CounterFrame CounterFrame (Counter (Counter c) c) {{ ... ... valuePanel.add(valueDisplay); valuePanel.add(valueDisplay); valueDisplay.setEditable(false); valueDisplay.setEditable(false); valueDisplay.setText(String.valueOf(c.getValue())); valueDisplay.setText(String.valueOf(c.getValue())); ... ... c.addObserver(this); c.addObserver(this); pack(); pack(); setVisible(true); setVisible(true); }} }} public public void void update update (Observable (Observable o, o, Object Object arg) arg) {{ Counter Counter cc == (Counter) (Counter) o; o; valueDisplay.setText(String.valueOf(c.getValue())); valueDisplay.setText(String.valueOf(c.getValue())); }} Ludwig-Maximilians-Universität München Prof. Hußmann Seite 13 Medientechnik – 1 - 61 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); } Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 62 Die Steuerung (Controller) class class ButtonController ButtonController implements implements ActionListener ActionListener {{ Counter Counter myCounter; myCounter; public public void void actionPerformed actionPerformed (ActionEvent (ActionEvent event) event) {{ String String cmd cmd == event.getActionCommand(); event.getActionCommand(); if if (cmd.equals("Count")) (cmd.equals("Count")) myCounter.count(); myCounter.count(); if if (cmd.equals("Reset")) (cmd.equals("Reset")) myCounter.reset(); myCounter.reset(); if if (cmd.equals("Exit")) (cmd.equals("Exit")) System.exit(0); System.exit(0); }} }} public public ButtonController ButtonController (Counter (Counter c) c) {{ myCounter myCounter == c; c; }} Ludwig-Maximilians-Universität München Prof. Hußmann Seite 14 Medientechnik – 1 - 63 Zähler-Beispiel: Anbindung des Controllers class class CounterFrame CounterFrame extends extends JFrame JFrame {{ ... ... JPanel JPanel buttonPanel buttonPanel == new new JPanel(); JPanel(); JButton JButton countButton countButton == new new JButton("Count"); JButton("Count"); JButton JButton resetButton resetButton == new new JButton("Reset"); JButton("Reset"); JButton JButton exitButton exitButton == new new JButton("Exit"); JButton("Exit"); }} public public CounterFrame CounterFrame (Counter (Counter c) c) {{ ... ... ButtonController ButtonController bc bc == new new ButtonController(c); ButtonController(c); countButton.addActionListener(bc); countButton.addActionListener(bc); buttonPanel.add(countButton); buttonPanel.add(countButton); resetButton.addActionListener(bc); resetButton.addActionListener(bc); buttonPanel.add(resetButton); buttonPanel.add(resetButton); exitButton.addActionListener(bc); exitButton.addActionListener(bc); buttonPanel.add(exitButton); buttonPanel.add(exitButton); ... ... }} Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 64 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); Ludwig-Maximilians-Universität München Prof. Hußmann Seite 15 Medientechnik – 1 - 65 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 extends WindowAdapter { public void windowClosing(WindowEvent event) { } } System.exit(0); Ludwig-Maximilians-Universität München Prof. Hußmann Medientechnik – 1 - 66 "Look-and-Feel" • Jede Plattform hat ihre speziellen Regeln für z.B.: – Gestaltung der Elemente von "Frames" (Titelbalken etc.) – Standard-Bedienelemente zum Bewegen, Schließen, Vergrößern, von "Frames" • Dasselbe Java-Programm mit verschiedenen "Look and Feels": Solaris (CDE) Windows Macintosh (MacOS X) Macintosh (Classic) • Einstellbares Look-and-Feel: Standard-Java oder plattformspezifisch Ludwig-Maximilians-Universität München Prof. Hußmann Seite 16 Medientechnik – 1 - 67