Von Enno Runne und Hendrik C.R. Lock
Der AWT (Abstract Window Toolkit) von Java bietet viele Möglichkeiten, um graphische Benutzeroberflächen zu bauen. Die vielleicht interessanteste Komponenente, die Java bei vielen Hackern erst beliebt gemacht hat, ist das Applet. Ein Applet ist eine relativ autarke Komponente, die über das Netz verschickt und in Browsern am anderen Ende angezeigt werden kann. In diesem Teil legen wir die Grundlagen, um mit Applets zu programmieren.
Frame
) aus AWT verwende,
so wird es auf jeder unterstützten
Plattform als ein Fenster mit Menuleiste dargestellt.
AWT erreicht diese Plattformunabhängikeit, indem es auf dem
jeweiligen
graphischen Subsystem aufsetzt. Dadurch sieht ein Fenster auf einem Mac
wie ein typisches
Mac Fenster aus, unter Windows NT entspricht es dem typischen
Microsoft Look&Feel,
und unter UNIX ist es oft ein X-Fenster.
Die Einhaltung der jeweiligen Look&Feel-Standards setzt
der Plattformunabhängigkeit
allerdings auch ihre Grenzen: Das Layout läßt
sich nämlich nicht immer
exakt nach den eigenen Wünschen fixieren,
weil z.B. die Randstärken von Fenstern oder Schattierungen
vom jeweiligen Zielsystem abhängen können.
In AWT programmierte Oberflächen können nicht nur als eigenständige Anwendungen ablaufen, sondern auch als sogenannte Applets verpackt in HTML-Seiten (Hyper Text Markup Language) eingebunden und über das Netz (WWW) verschickt werden. Applets sind Objekte einer AWT-Klasse, die von Java-fähigen Browsern geladen und ausgeführt werden können (z.B. vom Netscape-Browser oder dem Internet Explorer von Microsoft). Enthält eine HTML-Seite ein Applet, so lädt der Browser die Klassendatei mit dem ausführbaren Bytecode von der URL der HTML-Seite. Die Ausführung erfolgt dann interpretiert (und damit langsam), oder wird mit Hilfe eine JIT-Übersetzers beschleunigt (Just-in-Time z.B. beim Internet Explorer).
Der eigentliche Spaß an AWT und Applets besteht darin, dass sich viele Benutzer des WWW ihre HTML-Seiten mit Animationen und graphischem Zauber aufpeppen. Wie man das macht, besprechen wir in dieser und kommenden Ausgaben. Aber auch bei kommerziellen Anwendungen haben AWT und das Applet ihren besonderen Platz: Eingabeformulare in HTML-Seiten und interaktive Dialoge können so realisiert werden, dass Daten bereits vor ihrer Versendung übers Netz auf Konsistenz überprüft und verschlüsselt werden.
Im AWT wird ein Fenster durch ein
Objekt der Klasse
Window
oder Frame
erzeugt. Ein Window ist ein rahmenloses Fenster ohne Menuleisten,
während Frame ein Fenster mit Rahmen erzeugt, in das eine
Menuleiste eingehängt werden kann.
Rahmenlose Fenster werden vorwiegend für Fehlermeldungen
verwendet.
Folgende Anweisung erzeugt ein Fenster der Größe 300x200
und zeigt es an:
Frame f = new Frame(" Titeltext des Fensters "); f.resize(300, 200); f.show();
Bild 1 zeigt die Darstellung unter Solaris.
Die Klasse Component
bildet die Wurzel der
Klassenhierchie und legt damit die allgemeinen Methoden
aller Komponenten fest. Beispiele für solche Methoden
sind show()
und resize()
,
mit denen eine Komponente angezeigt b.z.w. in ihren
Ausmaßen verändert wird.
Bild 2 gibt die Komponentenhierarchie wieder.
Direkt unterhalb von Component
stehen die Grundkomponenten wie Button
,
Label
, Canvas
und
Scrollbar
.
Direkt von Component
erbt
die abstrakte Klasse
Container
, die einen Behältertyp einführt.
Ein Behälter kann viele weitere Komponenten enthalten,
z.B. können wir in ein Fenster mehrere Knöpfe einfügen.
Die Methode zum Einfügen heißt add()
.
Die wichtigsten Behälterklassen sind
Frame
und Panel
.
Wie bereits erwähnt, sind Frames
eigenständige Fenster, denen man
auch Menuleisten anheften kann.
Panels hingegen sind reine Behälter, die
nahtlos in der Komponenente aufgehen, in die sie eingefügt werden.
Damit sind Panels hervorragend für die Strukturierung graphischer
Oberflächen geeignet: zusammenhängende Knopfleisten und Texte
werden geschickterweise in ein gemeinsames Panel eingehängt,
und aus solchen Aggregaten lassen sich dann komplexere
Einheiten wie ein Mosaik aufbauen.
Applet
ist eine spezielle Unterklasse von Panel
,
die nicht nur als Behälter dient, sondern die enthaltenen
Komponenten auch in einem Browser anzeigbar macht.
Dazu werden zusätzliche Methoden benötigt, die in einem späteren
Teil ausführlicher behandelt werden sollen.
Unser erstes Applet setzt mit Hilfe eines Labels
den Text "Hallo Welt" auf einer HTML-Seite ab.
Durch die Methode setBackground()
wählen wir die Hintergrundfarbe
Weiss, so dass wir die vom Applet belegt Fläche sehen
können (vorausgesetzt, der Browser benutzt Grau als
Standardhintergrund).
Ausserdem wechseln wir die Schriftart und ihre Größe.
import java.applet.Applet; import java.awt.*; public class AppletBeispiel1 extends Applet { public AppletBeispiel1() { Label l1 = new Label( "Hallo Welt" ); add(l1); // ins Applet einhängen setBackground( Color.white ); resize( 200, 200 ); show(); // anzeigen } } |
<applet Codebase="code7" Code="AppletBeispiel1" Width=200 Height=50> </applet> |
<applet>
in eine HTML-Seite eingebunden.
Ihre Attribute Code
und
Codebase
geben dabei den Namen und den Pfad (die URL)
der class
-Datei an.
In unserem Beipiel liegt AppletBeispiel1
im Unterverzeichnis code2
relativ zur URL-Adresse
der HTML-Seite.
Die Attribute Width
und Height
geben dabei den Ausschnitt des
Applets an, der auf der Seite angezeigt wird.
Als erstes definieren wir ein Applet, auf dessen Fläche
Striche gezeichnet werden.
Ein Strich wird gezeichnet, solange die aktivierte Maus über
die Fläche gezogen wird.
Die Aktivierung der Maus wird durch die Methode
mouseDown()
erkannt. Wird die Maus aktiviert,
so sendet der AWT diese Methode an das Objekt desjenigen AWT-Elementes,
in dem die Maus gerade steht.
Genauso verhält es sich mit der Methode mouseDrag()
.
Sie wird von AWT aufgerufen, wenn sich die aktivierte Maus
in der Fläche unseres Applet bewegt.
In diesem Fall zeichnen also wir eine Linie
zwischen der vorangehenden und der aktuellen Mauskoordinate.
Um Linien graphisch zeichnen zu können, benötigen wir in AWT
ein Objekt der Klasse Graphics
.
Nun wird aber jede AWT-Komponente letztendlich mittels eines
Objektes der Klasse Graphics
dargestellt.
Deshalb können wir uns das zum Applet gehörende Graphikobjekt mit der
Methode getGraphics()
besorgen.
Der Methodenaufruf drawLine(x1,y1, x2, y2)
malt dann eine Linie im Graphikobjekt
zwischen den beiden Koordinaten. Die Farbe der Linie
ist durch die Farbe des Vordergrundes bestimmt; sie wird mit
setForeground()
gesetzt.
Ebenso wird die Hintergrundfarbe
durch setBackground()
bestimmt.
import java.applet.Applet; import java.awt.*; public class Skizze extends Applet { private int letztesX, letztesY; public Skizze() { setForeground( Color.blue); setBackground( Color.white ); resize( 600,350); } public boolean mouseDown(Event e, int x, int y) { letztesX = x; letztesY = y; return true; // Mausereignis wurde behandelt } /** zeichne Linienzüge bei gedrückter Maus */ public boolean mouseDrag(Event e, int x, int y) { Graphics g = getGraphics(); g.drawLine( letztesX, letztesY, x, y); letztesX = x; letztesY = y; return true; } }
Um dickere Linien zu erhalten, könnten wir auch Kreise am jeweiligen Ort der aktiven Maus malen:
g.fillOval(x,y, 8, 8 );
Nun stellt sich allerdings ein unbeabsichtigter Effekt ein: je schneller
wir die Maus ziehen, desto ausgedünnter werden die Linien, d.h. wir
erhalten keine durchgehenden Linienzüge.
Dies liegt daran, dass die Geschwindigkeit, mit der
mouseDrag
aufgerufen wird, so gering ist, dass nicht jede
Koordinate erfasst wird.
Damit haben wir bereits zwei Ereignisse und ihre Behandlung
kennengelernt: mouseDown
und mouseDrag
.
Im nächsten Abschnitt erweitern wir die Ereignisbehandlung
auf Schalter.
Button
und Checkbox
ein.
Der Code new Button("Beschriftung")
erzeugt einen beschrifteten Knopf, der per Mausklick aktiviert werden
kann.
Bei seiner Aktivierung wird eine Ereignisbehandlung ausgelöst.
Sie besteht darin, dass die Methode action()
derjenigen innersten Komponente aufgerufen wird, in die
der Knopf eingehängt worden ist.
Weil die Methode action()
allerdings auch bei vielen
anderen Ereignissen aufgerufen wird, müssen wir den Knopfdruck
identifizieren.
Nichts ist einfacher als das: wir merken uns den Verweis auf unseren
Schalter und vergleichen ihn in action()
mit dem Verweis des verursachenden Objektes.
Das verursachende Objekt wird durch den Code
event.target()
identifiziert, wobei event
ein Objekt der Klasse
Event
ist, das alle Informationen über das Ereignis
transportiert.
Konnte das Ereignis behandelt werden, so geben wir in
action()
den Wert true
an
den Aufrufer zurück. Im anderen Fall geben wir false
zurück, womit wir der umgebenden Komponente signalisieren, dass sie
das Ereignis behandeln soll.
Falls keine Komponente in dieser aufsteigenden Kette
das Ereignis behandelt (es ist die Kette,
in der Komponenten ineinander eingehängt worden sind),
so wird es einfach ignoriert.
Für die Auswahl der Farbstifte brauchen wir nun Schalter,
die sich "erinnern" können,
dass sie bereits gedrückt worden sind.
Der Schalter vom Typ
Button
kann das allerdings nicht.
Außerdem soll immer nur einer aus vielen solcher Schalter
gedrückt sein. Diese häufig anzutreffende
Funktionalität
bieten im AWT die Klassen Checkbox
und
CheckboxGroup
.
Der folgende Code
CheckBox rot = new Checkbox( "rot", gg, false );
erzeugt einen Schalter mit der Beschriftung "rot".
Das Argument gg
ist dabei ein Objekt der Klasse
CheckboxGroup
.
Durch Bindung einer Reihe von Schaltern an eine solche
Gruppe wird erreicht, dass immer nur ein einziger Schalter
der Gruppe gedrückt sein kann.
Das letzte Argument bestimmt, ob der Schalter initial
gedrückt ist, oder nicht.
import java.applet.Applet; import java.awt.*; public class Skizze3 extends Skizze { private Button clear, red; private Checkbox rot, blau, sch; public Skizze3() { super(); Panel p = new Panel(); p.setBackground(Color.pink); add(p); CheckboxGroup g = new CheckboxGroup(); rot = new Checkbox( "rot", g, false ); blau = new Checkbox( "blau", g, true ); sch = new Checkbox( "sch", g, false ); p.add( blau ); p.add( rot ); p.add(sch); clear = new Button("Löschen"); Panel p2 = new Panel(); p2.setBackground(Color.red); // rot unterlegen p2.add(clear); p.add(p2); } public boolean action( Event e, Object o ) { if (e.target == clear ) { Graphics g = getGraphics(); update(g); return true; } else if (e.target == rot ) { setForeground( Color.red ); return true; } else if (e.target == blau ) { setForeground( Color.blue ); return true; } else if (e.target == sch ) { setForeground( Color.black ); return true; } return false; } }
Ach ja, nun bleibt nur noch zu erwähnen,
dass die neue Klasse ihre Funktionalität zum Malen
von der Klasse Skizze
erbt.
Eine Demo wird im Bild 4 wiedergegeben.