Grafical User Interface
Im Unterschied zur text-basierten Interaktion wird mit GUI eine
Benutzerschnittstelle bezeichnet, die eine grafische Oberfläche verwendet
und Interaktion mit der Maus ermöglicht. Java GUI Komponenten dienen zum
Erstellen von Fenstern, Buttons, Grafiken usw. Für viele GUI Komponenten
gibt es im Java API zwei Versionen:
- AWT: deprecated, Warnung beim Compilieren. Interessant für
Kompatibilität in Applets mit älteren Browser-Versionen, sonst
entsprechende Komponenten in javax.swing.* verwenden.
- Swing: aktuelle Version der AWT-Komponenten mit besserer Implementierung.
Namensprefix J, also JFrame, JButton usw.
NICHT in derselben Applikation beide verwenden!
Begriffe:
- Komponenten: Frame, Panel, Button, ... Elemente für die
GUI-Gestaltung. Diese Objekte haben eine grafische Darstellung, d.h. z.B.
mit new JFrame() wird ein Fenster erzeugt.
Komponenten können verschachtelt werden, z.B. ein Frame enthält zwei
Panels, diese wiederum jeweils mehrere Buttons. Dazu wird die add() Methode verwendet.
- Layouts: Varianten für die Anordnung der Komponenten, die auch bei
Änderung der Fensterform gelten, z.B. nebeneinander (FlowLayout, default),
North/South/East/West/Center (BorderLayout), in Zeilen und Spalten (GridLayout) usw.
- Events: wie soll auf Mouseclicks usw. reagiert werden. Dazu werden Objekte
als Listener definiert, die das entsprechende Interface implementieren, indem
z.B. die Methode actionPerformed() im Interface ActionListener definiert wird.
SwingDemo
import javax.swing.*;
public class SwingDemo extends JFrame {
public static void main(String[] args) {
new SwingDemo("SwingDemo");
}
SwingDemo(String title) {
super(title);
JLabel label = new JLabel("This is the Swing Demo!");
getContentPane().add(label);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
- SwingDemo ist eine Unterklasse von JFrame, und damit ein Top-Level
Component mit grafischer Darstellung (Fenster).
- In solche Top-Level-Komponenten werden weitere Komponenten in den ContentPane
eingefügt, zB hier der JLabel. In andere Komponenten wird direkt mit add()
eingefügt.
Layouts, Events
Layouts ienen zur Anordnung der Elemente, die auch nach Resize des Fensters
erhalten bleiben soll.
- FlowLayout: Elemente werden von links nach rechts aufgereiht.
Beispiel: FlowLayoutDemo.java
- BorderLayout: Elemente werden in Nord/Süd/Ost/West/Zentrum plaziert.
Beispiel: BorderLayoutDemo.java
- GridLayout: tabellenförmige Anordnung
Events wie Click auf Button, Window Close usw. Events können mit
ActionListeners behandelt werden.
Beispiel: SwingCounterDemo. Bei dieser
Variante muß unsere Klasse das Interface ActionListener() implementieren,
mit der Methode actionPerformed(), wo wir die einzelnen Aktionen behandeln.
Die Verbindung zwischen den einzelnen GUI-Elementen und der
actionPerformed() Methode stellen wir mit addActionListener() her. Da
SwingCounterDemo das ActionListener() Interface implementiert, können wir
'this' als ActionListener verwenden.
Alternativ können wir den ActionListener auch als inner class definieren:
final JTextField tf = new JTextField(5);
tf.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s = tf.getText();
}
});
Weitere Beispiele: noch eine Swing-Variante des
Counters, und eine AWT-Variante.
Grafik
Natürlich stehen in Swing und AWT auch grafische Grundoperationen zur
Verfügung wie das Zeichnen von Linien sowie Rechtecken und anderen
geometrischen Formen. Dazu gibt es verschiedene Möglichkeiten; in unserem
Beispiel wird die eigene Klasse MyClass als JPanel definiert und überschreibt
dann die paint() Methode, die als Parameter den aktuellen graphics context
erhält und Zeichenoperationen ausführt:
class MyClass extends JPanel {
...
public void paint(Graphics g) {
...
g.drawLine(10,30,100,200);
}
}
Bei den Koordinaten ist zu beachten, daß der Ursprung links OBEN liegt.
Die paint() Methode wird NICHT von unserer Applikation aufgerufen, sondern
von der JRE, und zwar immer dann, wenn es notwendig ist, d.h. wenn die
Komponente tatsächlich neu gezeichnet werden muß; z.B. wenn eine Applikation
gestartet wird, aber auch wenn ein Fenster wieder sichtbar wird, das vorher
verdeckt war, usw.
Ist von der Applikationslogik aus anderen Gründen ein Neuzeichnen
erforderlich, dann kann das mit repaint() bewirkt werden.
In ThreadTest.java sehen Sie, wie das mit
einem Thread gelöst werden kann, der in periodischen Abständen
unabhängig von sonstigen Interaktionen das Neuzeichnen übernimmt.
Aufgabe 1: Finden Sie in der Java API Dokumentation heraus, welche
Operationen mit einem Graphics Objekt möglich sind, und schreiben Sie eine ganz
einfache kleine Applikation, die einige Linien und andere grafische Elemente
zeichnet.
Aufgabe 2: Setzen Sie auch die GUI-Elemente wie Buttons usw. ein,
damit die Grafik interaktiv veränderbar wird, z.B. Ändern der Farbe.
Aufgabe
Wir wollen die GUI Komponenten in einer etwas umfangreicheren Aufgabe
einsetzen. Es geht um die Steuerung eines Bootes.
Folgendes soll implementiert werden:
- Ein Boot wird als bewegliches Objekt auf einer Wasserfläche
dargestellt. Es hat die Eigenschaften Standort (X/Y Koordinaten),
Geschwindkeit in Knoten, und Richtung (Kurs in Grad von 0 bis 360).
- Gesteuert wird mit dem Steuerrad, das kurz nach Steuerbord
und Backbord gelegt werden kann, was jeweils eine
Kursänderung von 11.25 Grad (1 Strich) bewirkt. Zwei Buttons sind ausreichend.
- Bei Fahrt unter Motor kann die Geschwindigkeit gewählt werden. Es soll folgende
Möglichkeiten (Buttons) geben:
- off (0 Knoten)
- slow (1 Knoten)
- fast (5 Knoten)
- Die Bewegung das Bootes wird durch Neuzeichnen in bestimmten Zeitintervallen
dargestellt (z. B. alle 50 Millisekunden):
- Das können wir als Thread realisieren.
- In der Methode run() können wir nun alle nötigen Berechnungen vornehmen und
- mit repaint() die Komponenten neu zeichnen lassen.
- Die Komponenten definieren soweit nötig
ihre eigene paint() Methode (siehe API Doc der Swing Komponenten, z.B. JPanel)
- Der aktuelle Wind wird als Pfeil im rechten oberen Eck des
Navigationsbereichs dargestellt.
- Richtung und Stärke beginnen mit zufälligen Werten und ändern sich
langsam.
- Die Windstärke wird in Beaufort angegeben und soll zwischen 0 und 9
liegen.
- Die Windrichtung wird in Grad angegeben, aus der der Wind kommt, d.h. Wind mit
Bft 2 aus 045 ist ein leichter NO-Wind. Der Pfeil soll aber in die Richtung weisen,
in die der Wind weht. Richtung und Stärke sollen in kleiner
Schrift bei der grafischen Darstellung mit angegeben werden.
- Das Boot wird nicht als Kreis dargestellt, sondern in einer Form, die Bug und Heck erkennen
läßt. Außerdem soll es gemäß dem aktuellen Kurs ausgerichtet sein, d.h. bei z.B. Kurs 090 soll
der Bug nach rechts weisen.
- Bei Fahrt unter Segel ergibt sich die Geschwindigkeit in Knoten aus
dem Winkel zwischen Kurs und Wind gemäß Diagramm. Ein Knoten entspricht einer Seemeile
pro Stunde, das sind 1.85 km/h.
- Alle Parameter, die im Code bisher als Konstanten definiert
wurden, sollen über das GUI geändert werden können. Dabei können Textfelder,
aber auch Pulldown menues, Check boxes, Radio buttons usw. eingesetzt werden.
- Die Trägheit des Bootes wird realistisch
modelliert, d.h. die Geschwindigkeit in Knoten
ändert sich nur langsam, wenn sich der Vortrieb ändert.
- Unter Motor kann auch auf Rückwärtsfahrt geschalten werden,
dann wirkt zunächst nur der Radeffekt als seitliches Auswandern
des Hecks (nach backbord), erst nach einigen Sekunden beginnt
das Boot rückwärts Fahrt aufzunehmen, und erst dann beginnt das Ruder
zu wirken.
- Das Boot ist nicht allein auf dem Wasser, sondern es gibt auch
eine konfigurierbare Anzahl anderer Segelboote, die sich zufaellig bewegen, allerdings
- realistisch entsprechend dem aktuellen Wind, und
- ohne das Fenster zu verlassen.
- Kollisionen von Booten werden entdeckt, und die Weiterfahrt
beider Boote wird entsprechend beeinflußt.
Der gesamte Code wird mit javadoc dokumentiert, d.h. alle Klassen und Methoden
werden mit Kommentaren versehen.