JAVA und die grafische Benutzeroberfläche
Alfred Nussbaumer
Das Ereignismodell von JAVA wurde in PCNEWS-92 an Hand von Tastaturereignissen bereits kurz vorgestellt (vgl [2]). Neben diesen gehören Mausereignisse und Ereignisse in Zusammenhang mit dem Anwendungsfenster zu den elementarsten Ereignissen. Sie werden in einfachen Beispielen in diesem Artikel besprochen.
Fensterereignisse treten auf, wenn sich der Zustand eines Fensters ändert. Sie werden somit von einem Window-Event ausgelöst und entweder von einem WindowListener-Objekt oder von einem WindowAdapter-Objekt entgegen genommen. Das folgende Beispiel läuft im Gegensatz zu Applets (vgl. [2]) in einem eigenen Anwendungsfenster ab. Die Grundlage dafür bietet die Klasse JFrame, die die für Anwendungsfenster typische Umgebung liefert: Das Fenster hat auf eine bestimmte Position und Größe auf einem bestimmten Bildschirm, einen Eintrag in seiner Titelleiste und kann dem verwendeten Betriebssystem entsprechend beispielsweise verschoben, minimiert, maximiert, eingerollt, geschlossen,... werden. Die eigene Anwendung erweitert die Klasse JFrame und erbt somit seine Eigenschaften.
Im folgenden JFrame-Beispiel Draw1.java dient die Adapterklasse WindowAdapter dazu, das Fensterereignis „WINDOW_CLOSING“ zu verarbeiten: In diesem Fall wird das laufende Programm durch den Aufruf der System-Methode exit() beendet.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Draw1 extends JFrame {
public Draw1() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
}
public static void main(String[] args) {
Draw1 proggi = new Draw1();
proggi.setTitle("... ein leeres Fenster ...");
proggi.setSize(400,300);
proggi.setLocation(100,100);
proggi.show();
}
}
Die Adapterklasse WindowAdapter wird dem JFrame-Objekt im Konstruktor mit Hilfe der Window-Methode addWindowListener hinzugefügt. Im Hauptprogramm sorgt die Window-Methode show() dafür, dass das Fenster angezeigt wird. Die Methoden setSize() und setLocation() werden von der Oberklasse Component vererbt – sie legen die Fenstergröße und ihre Position fest. Die Methode setTitle() übergibt eine Zeichenkette an die Titelleiste des Anwendungsfensters.
Die Anwendung Draw1 verläuft unspektakulär: Das Fenster wird an der angegebenen Position mit einem 400 Pixel mal 300 Pixel großen Bereich ausgegeben. Es hat – abhängig vom verwendeten Betriebssystem – typische Fenstereigenschaften. Beim Schließen des Fensters wird die mit der Anwendung gestartete Java Virtual Machine gestoppt.
Die Fensterereignisse, die beispielsweise auftreten, wenn ein Fenster deaktiviert oder aktiviert wird, wenn es auf eine minimale Größe verkleinert oder wieder geöffnet wird, usf. werden in der Java-Dokumentation (vgl. [1]) detailliert beschrieben.
Mausereignisse treten auf, wenn eine bestimmte Maustaste gedrückt wurde, oder wenn die Maus über ein Objekt gezogen wird. Dazu kommen noch Ereignisse, die in Zusammenhang mit dem Mausrad auftreten. Die folgenden Interfaces und die dazu passenden Adapterklassen werden verwendet:
•mouseClicked(MouseEvent e) - wird bei einem Mausklick ausgelöst. Dies ist der Fall, wenn eine Maustaste gedrückt und anschließend wieder ausgelassen wurde.
•mouseEntered(MouseEvent e)– wird ausgelöst, wenn der Mauszeiger den Bereich eines Objekts erreicht
•mouseExited(MouseEvent e)– wird ausgelöst, wenn der Mauszeiger den Bereich eines Objekts verlässt
•mousePressed(MouseEvent e) – wird ausgelöst, wenn der Mauszeiger auf ein Objekt weist und die Maustaste gedrückt wird
•mouseReleased(MouseEvent e) – wird ausgelöst, wenn die gedrückte Maustaste wieder losgelassen wird.
•mouseDragged(MouseEvent e)– wird ausgelöst, wenn der Mauszeiger bei gedrückter Maustaste bewegt wird.
•mouseMoved(MouseEvent e) – tritt auf, wenn der Mauszeiger bewegt wird.
•mouseWheelMoved(MouseWheelEvent e)– tritt auf, wenn das Mausrad betätigt wird.
Die Klasse MouseEvent enthält dabei Methoden wie getButton(), getPoint(), getX() oder getY(). Alle Objekte und Methoden können in der JAVA-Dokumentation ([1]) genau nachgelesen werden.
Mit einem Mausklick sollen auf einem leeren Rechteck kleine ausgemalte Kreise gezeichnet werden. Außerdem sollen die Koordinaten des Mauszeigers beim Klick ausgegeben werden. Die Ausgabe aller Objekte erfolgt mit Hilfe eines so genannten Containers, der im vorliegenden Fall den gesamten Inhalt des Fensters („ContentPane“) aufnimmt[1].
Draw2.java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Draw2 extends JFrame implements MouseListener {
private int xpos;
private int ypos;
public Draw2() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
Container c = getContentPane();
c.setLayout(new FlowLayout());
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
xpos = e.getX();
ypos = e.getY();
repaint();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillRect(0,0,400,300);
g.setColor(Color.green);
g.fillOval(xpos,ypos,10,10);
g.setColor(Color.red);
g.drawString("(" + (xpos+5) + "/" + (ypos +5) + ")",xpos+5,ypos-5);
}
public static void main(String[] args) {
Draw2 proggi = new Draw2();
proggi.setTitle("Punkte mit der Maus setzen");
proggi.setSize(400,300);
proggi.setLocation(100,100);
proggi.show();
}
}
Die Klasse Draw2 implementiert das Interface MouseListener. Es enthält die Methoden zu den Mausereignissen: mouseClicked(MouseEvent e), mouseEntered(MouseEvent e), mouseExited(MouseEvent e), mousePressed(MouseEvent e) und mouseReleased(MouseEvent e)[2]. Diese Methoden müssen nun mit dem Code versehen werden, der die gewünschte Aktion ausführt. Im Beispiel wird lediglich die Methode mouseClicked() kodiert: Zunächst wird die aktuelle Position des Mauszeigers mit den beiden MouseEvent-Methoden getX() und getY() ermittelt und an die Klassenvariablen xpos und ypos übergeben; anschließend wird der Fensterinhalt durch die repaint() - Methode neu ausgegeben.
[punkte.jpg]
Zu den im letzten Abschnitt aufgezählten Mausereignissen kommen noch die beiden Ereignisse, die mit dem Bewegen der Maus zusammen hängen: mouseMoved(MouseEvent e), wenn die Maus bewegt wird, und mouseDragged(MouseEvent e), wenn die Maus mit gedrückter linker Maustaste über den Bildschirm „gezogen“ wird.
Im folgenden Beispiel wird wieder die Verwendung von Adapterklassen demonstriert: Da die Adapterklassen leere Deklarationen aller erforderlichen Methoden enthalten, genügt es, im eigenen Programm nur die Methoden zu verwenden, für die man einen eigenen Programmcode vorgesehen hat. Im Beispiel werden auf diese Weise die drei Methoden setze(), ziehe() und loese() verwendet: Jedes Rechteck wird dabei durch die Koordinaten seines linken, oberen Eckpunktes und durch die Koordinaten seines rechten, unteren Eckpunktes festgelegt. Beim Drücken der linken Maustaste werden die Koordinaten des linken, oberen Eckpunktes übernommen und in der Klassenvariablen punktelio[][] gespeichert. In diesem zweidimensionalen Array werden die x- und y-Koordinaten der linken oberen Eckpunkte aller gezeichneten Rechtecke gespeichert (setze()). Das „Aufziehen der Rechtecke“ bereitet die meiste Mühe – man beachte dabei die Verwendung der Klassenvariablen dragged, die lediglich den Status der Mausereignisse speichert (ziehe()): So lange die Methode ziehe() über den MouseMotionListener aufgerufen wird, erhält die Stautsvariable dragged den Wert true. In diesem Fall wird nach dem Löschen des Bildschirmes und dem Zeichnen aller bestehenden Rechtecke in blauer Farbe das gerade „aufgezogene“ Rechteck in grüner Farbe gezeichnet. Nach dieser Ausgabe wird die Statusvariable dragged auf false gesetzt. Die Koordinaten des rechten, unteren Eckpunktes werden exakt beim Auslassen der Maustaste in der Klassenvariablen punktereu[][] gespeichert (loese()); da die Statusvariable dragged in diesem Fall nicht verändert wird und daher den Wert false aufweist, entfällt beim neuerlichen Bildschirmaufbau die Ausgabe des grün gezeichneten Rechtecks.
Draw3.java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Draw3 extends JFrame {
int punktelio[][] = new int [40][2];
int punktereu[][] = new int [40][2];
int punkteint[][] = new int [1][2];
int zaehler = 0;
boolean dragged = false;
Color lightgray = new Color(220,220,220);
public Draw3() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
Container c = getContentPane();
c.setLayout(new FlowLayout());
this.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
setze(me);
}
public void mouseReleased(MouseEvent me) {
loese(me);
}
});
this.addMouseMotionListener(
new MouseMotionAdapter() {
public void mouseDragged(MouseEvent me) {
ziehe(me);
}
});
}
public void setze(MouseEvent me) {
if (zaehler < 40) {
punktelio[zaehler][0] = me.getX();
punktelio[zaehler][1] = me.getY();
}
}
public void ziehe(MouseEvent me) {
if (zaehler < 40) {
punkteint[0][0] = me.getX();
punkteint[0][1] = me.getY();
dragged = true;
repaint();
}
}
public void loese(MouseEvent me) {
if (zaehler < 40) {
punktereu[zaehler][0] = me.getX();
punktereu[zaehler][1] = me.getY();
zaehler++;
repaint();
}
}
public void paint (Graphics bs) {
if (zaehler <= 40) {
bs.setColor(Color.white);
bs.fillRect(0,0, 400, 400);
bs.setColor(lightgray);
bs.fillRect(0, 400, 400, 450);
bs.setColor(Color.blue);
for (int i=0; i<zaehler; i++) {
int breite = punktereu[i][0] - punktelio[i][0];
int hoehe = punktereu[i][1] - punktelio[i][1];
bs.drawRect(punktelio[i][0], punktelio[i][1], breite, hoehe);
}
}
if (dragged) {
bs.setColor(Color.green);
int breited = Math.abs(punkteint[0][0] - punktelio[zaehler][0]);
int hoehed = Math.abs(punkteint[0][1] - punktelio[zaehler][1]);
bs.drawRect(punktelio[zaehler][0], punktelio[zaehler][1], breited, hoehed);
dragged = false;
}
bs.setColor(Color.red);
bs.drawString(zaehler + " Rechtecke gezeichnet", 10, 430);
if (zaehler==40) bs.drawString("und aus! (Speicher voll)", 200, 430);
}
public static void main(String[] args) {
Draw3 proggi = new Draw3();
proggi.setTitle("Rechtecke aufziehen");
proggi.setSize(400,450);
proggi.setLocation(100,100);
proggi.show();
}
}
Im vorliegenden Beispiel werden die Daten in einzelnen Arrays mit einer feststehenden Größe gespeichert. Aus diesem Grund wird vor jeder Aktion überprüft, ob der Speicherplatz noch ausreicht. Die Gesamtzahl aller bereits gezeichneten Rechtecke wird deshalb in einer Statuszeile ausgegeben:
[rechtecke.jpg]
Zeichenprogramme unterscheiden sich von den hier vorgestellten Beispielen gewaltig. Dennoch können einige überschaubare Aufgaben bewältigt werden:
1.Weitere Grafikobjekte (Linien, Kreise, Polygone, etc.) sind zu verwenden. Die Auswahl der Objekte kann beispielsweise über einen Tastaturcode erfolgen...
2.Grafikobjekte sollen im Nachhinein ausgewählt und beispielsweise gelöscht werden können.
3.Verschiedene Grafikobjekte, Zeichenfarben, Pinselstärken und andere Eigenschaften (beispielsweise geschlossene Objekte ausfüllen) sollen über ein Menü-System zur Auswahl gestellt werden. Die gewählten Eigenschaften sollen (in Kurzform) in der Statuszeile ausgegeben werden.
4.Bestimmte Eigenschaften (Pinselauswahl, Zeichenfarbe, etc.) sollen mit Hilfe von Schaltflächen zur Auswahl angeboten werden. Diese Schaltflächen bilden zusammen eine Reihe von Tools entlang einer senkrechten Bildschirm-Fensterreihe.
Nicht alle Aktionen, die „mit der Maus ausgeführt“ werden sind an MouseEvents gebunden. In diesem Artikel haben wir das Bewegen der Maus, das Ziehen und das Drücken der Maustasten behandelt, die mit den JAVA-Interfaces MouseListener und MouseMotionListener entgegengenommen werden. Für den Klick auf eine Schaltfläche verwenden wir beispielsweise einen ActionListener, einen ItemListener für die Auswahl eines Auswahlkästchens oder einen AdjustmentListener für die Position eines Scrollbalkens. Beispiele dazu sollen zu einem späteren Termin hier vorgestellt werden.
[1] http://java.sun.com/j2se/1.4.2/docs/index.html (Dokumentation aller verfügbaren Packages)
[2] PCNEWS 92, S. 24 („JAVA und die grafische Benutzeroberfläche“)
[3] Herbert Schildt, „Java 2 Ent-Packt“, mitp-Verlag
[4] Christian Ullenboom, „Java ist auch eine Insel“, Galileo Computing
[5] http://www.gymmelk.ac.at/nus/informatik/wpf/JAVA (Unterrichtsbeispiele zum Programmieren mit JAVA)