![]() |
|
Mittels der bisher vorgestellten Programmierfunktionalität ist es ohne weiteres möglich, das Spiel „Schiffe versenken" zu implementieren, wenn sämtliche Ein- und Ausgaben über die Kommandozeilenschnittstelle vorgenommen werden. Offensichtlich ist aber eine derartige Implementierung mehr als ineffizient, da sich der Benutzer bspw. merken muss, wo er seine Schiffe platziert hat. Einer der großen Vorteile von Java ist es, dass diese Sprache weit reichende Möglichkeiten zur Verarbeitung von Grafiken, Bildern, Audio und Benutzerschnittstellen zur Verfügung stellt – eine wesentliche Voraussetzung für die Implementierung des Spiels. In Java steht zur Implementierung von Benutzeroberflächen das Abstract Windowing Toolkits (AWT) zur Verfügung. Grundsätzlich beinhalten Benutzeroberflächen eine statische und eine dynamische Funktionalität. Während sichtbare Elemente wie bspw. Bilder oder Knöpfe zunächst einen statischen Charakter aufweisen, kommt eine dynamische Funktionalität dann ins Spiel, wenn Elemente eine Funktion beinhalten. Während ein Bild lediglich betrachtet werden kann, kann ein Knopf bedient werden. Das Drücken eines Knopfs löst dann ein Ereignis, einen sog. Event, aus. Ziel dieses Teilkapitels ist die Darstellung der statischen Funktionalität, wohingegen die Verarbeitung von Events im nächsten Teilkapitel erläutert wird. In diesem Teil des Buches wird daher zuerst erklärt, wie Benutzeroberflächen zu entwickeln sind. Diese sind wohlgemerkt ohne dynamische Komponenten zunächst nutzlos. Eine brauchbare Implementierung kann daher nur erfolgen, wenn die Kenntnisse beider Teilkapitel eingesetzt werden. Die Beispiele, die im Folgenden angegeben werden, beziehen sich grundsätzlich auf Applets. Eine Unterscheidung zwischen Applet und Application macht in diesem Zusammenhang wenig Sinn, da die notwendigen Konzepte für beide Anwendungsarten identisch gültig sind. Beachtet man die bereits beschriebenen Unterschiede zwischen Applet und Application, so kann jede der beschriebenen Benutzeroberflächen problemlos auch für eine Application angewendet werden. Zuerst wird erläutert, wie in Java Grafiken, Schriftarten und Farben, also die Grundbausteine von Benutzeroberflächen, verwendet werden. Grafiken in Java Grundsätzlich kann man ein Applet als eine Art von Container betrachten, der Platz für grafische Inhalte bietet. Die meisten Routinen, die zur Erzeugung von Grafikobjekten notwendig sind, sind in der Klasse Graphics enthalten, die Teil des AWT (Klasse java.awt) ist. Alle Applets, die Grafik verwenden, müssen daher diese Klasse importieren. Ein Grundgerüst, das in jedem derartigen Applet zu finden ist, lautet:
import java.awt.Graphics; public void paint (Graphics screen) { //Aktion } } Die Methode paint, die Teil der Klasse Graphics ist, realisiert Bildschirmausgaben derart, dass eine beliebige Ausgabe durch ein Überschreiben der Methode paint implementiert wird. Hierzu ist es notwendig, ein Graphics-Objekt zu erzeugen, das die notwendigen Routinen zur Generierung zur Verfügung stellt. Das folgende Beispiel erzeugt bspw. die Ausgabe „Willkommen in Java". Die Parameter, die die dazu notwendige Methode drawString erwartet, sind ein String und die Bildschirmkoordinaten, an denen die Ausgabe erfolgen soll. Es sei angemerkt, dass drawString Teil der Klasse Graphics ist, was auch an der Referenzierung über die Variable screen erkennbar wird.
import java.awt.Graphics; public void paint (Graphics screen) { screen.drawString("Willkommen in Java", 10,10); } } Jedes Mal, wenn ein Applet angezeigt wird, wird die paint-Methode durchlaufen und dadurch die gewünschte Ausgabe erzeugt. Das von Java verwendete Koordinatensystem ist derart aufgebaut, dass der Ursprung (Koordinate (0, 0)) in der linken oberen Ecke angesiedelt ist. x-Koordinaten, die in Koordinaten jeweils links angegeben werden, spezifizieren die horizontale Position eines Elements, y-Koordinaten entsprechend die vertikale Position. Maßeinheit ist in Java grundsätzlich das Picture Element (Pixel). Zum Zeichnen grafischer Objekte unterscheidet Java zwischen Methoden, die Objekte zeichnen und solchen, die vorliegende Objekte mit einer Farbe ausfüllen. Java verwendet die folgenden Routinen zum Zeichnen von Objekten:
drawLine erwartet vier Argumente: die x- und y-Position des Ausgangspunkts der Linie und respektive die x- und y-Position des Endpunkts. Die folgende Zeile erzeugt bspw. eine Linie von (0, 0) nach (100, 100). Das entsprechende Applet ist leicht zu erzeugen, wenn die Zeile in die paint-Methode aufgenommen wird.
screen.drawLine(0,0,100,100);
Hierbei wurden zunächst zwei Arrays mit den x- und y-Koordinaten angelegt. Anschließend wurde die Anzahl der Punkte in der Variablen punkte gespeichert und das Polygon erzeugt. Sollen einem Polygon nun weitere Punkte hinzugefügt werden, so wird die Methode addPoint, die Teil der Klasse Polygon ist, verwendet. addPoint erwartet die x- und y-Koordinate des hinzuzufügenden Punkts als Argument. Das Kommando p.addPoint(20,20) fügt daher dem oben beschriebenen Polygon den Punkt (20,20) hinzu. Nachdem ein Polygon erzeugt wurde, kann es entweder mit drawPolygon oder mit fillPolygon angezeigt werden. Grundsätzlich ist zu beachten, dass zur Verwendung von Polygonen die Klasse java.awt.Polygon zu importieren ist. Das folgende Beispiel illustriert die Anzeige des oben generierten Polygons.
int x[] = {0,10,10,0};
Abb. 4.6: Winkelangaben in Java Um die Implementierung von Grafiken zu beschleunigen, stehen in Java zusätzlich die Methoden copyArea und clearRect zur Verfügung. Mittels copyArea können rechteckige Bereiche eines Applet-Fensters auf andere Bereiche dieses Fensters kopiert werden. copyArea erwartet sechs Argumente:
Das folgende Beispiel kopiert eine Region der Größe 50x50 Pixel, die sich derzeit an der Position (60,60) (linke obere Ecke) befindet, um 20 Pixel nach links und um 30 Pixel nach unten: Mittels der clearRect-Methode wird ein Bildschirmbereich mit der Farbe gefüllt, die der Hintergrundfarbe des Applets entspricht. clearRect erwartet dieselben vier Parameter wie drawRect. Soll bspw. der ganze Bildschirmbereich gelöscht werden, so kann die getSize-Methode verwendet werden, die die Breite und Höhe des Bereichs als Ergebnis zurückgibt. Das folgende Beispiel zeigt, wie der gesamte Bildschirmbereich zu löschen ist.
screen.clearRect(0,0,getSize().width, getSize().height); Zum Abschluss dieses Abschnitts wird ein Beispiel angegeben, das ein Spielfeld für das Spiel „Schiffe versenken" erzeugt. Der Leser sollte mittlerweile über die notwendigen Kenntnisse verfügen, dieses Beispiel zu verstehen.
import java.awt.Graphics; public void paint (Graphics screen) { int i; screen.drawLine(10, 10+i*30,310, 10+i*30); // Erzeuge vertikale Linien screen.drawLine(10+i*30, 10,10+i*30, 310); } } Farbsetzung in Anwendungen Zur Beschreibung von Farben werden üblicherweise sog. Farbräume verwendet, bspw. RGB, HSV oder YCrCb [Stei98]. Java unterstützt den RGB (rot–grün–blau)-Farbraum, in dem Farben als Kombination aus rot, grün und blau angegeben werden. Für jede Farbkomponente steht hierbei ein Byte zur Verfügung, mit dem 256 verschiedene Werte spezifizierbar sind. Schwarz wird bspw. als (0,0,0) und weiß als (255,255,255) angegeben. Bei Weiß sind daher alle Farben in maximaler Intensität vorhanden.
Abb. 4.7: Screenshot des Spielfelds Java verwendet die Klassen Color und ColorSpace als Teil des Packages java.awt, um Anwendungen Farben zuzuweisen. Während Color die Verarbeitung von Farben betrifft, können mit ColorSpace auch andere Farbräume angegeben werden. In diesem Buch soll jedoch stets der RGB-Farbraum verwendet werden. Zum Setzen von Farben muss entweder ein Color-Objekt generiert werden, das eine Farbe repräsentiert, oder eine der Standardfarben verwendet werden, die Teil der Klasse Color sind. Der Konstruktor dieser Klasse kann auf zwei verschiedene Arten aufgerufen werden: Als Kombination eines Tripels von Ganzzahlen (Integers), die die RGB-Komponenten angeben oder als Tripel aus Fließkommazahlen, die den prozentualen Anteil des Zahlenbereichs von 0 bis 255 angeben.
//RGB-Farbe Schwarz // RGB-Farbe Weiss Im obigen Beispiel wurde jeweils ein F an die eigentliche Zahl angehängt, da es sich um Fließkommazahlen (floats) handelt. Farben können nun mit Hilfe der Methode setColor gesetzt werden, die ebenfalls Teil der Klasse Graphics ist und daher in einer paint-Methode Anwendung findet. Tab. 4-1 gibt einige gebräuchliche Farbkombinationen an. |
|
|
Tab. 4.1: RGB-Kombinationen Das folgende Beispiel illustriert, wie eine Farbe gesetzt wird. Hierbei werden auch der Vorder- und der Hintergrundbereich auf verschiedene Farben gesetzt, wozu die Methoden setForeground und setBackground verwendet werden, die Teil der Klasse Applet sind.
// Orange Meist werden Vorder- und Hintergrund eines Applets in der init-Methode gesetzt, da sich diese Farbe normalerweise im Lebenszyklus des Applets nicht ändert. Soll herausgefunden werden, welche Farbe ein Objekt hat, so kann die Methode getColor verwendet werden, die Teil der Klasse Graphics ist, bzw. getForeground und getBackground als Teil der Klasse Applet. Das folgende Beispiel legt dar, wie die Vordergrundfarbe auf den Wert eingestellt wird, den auch der Hintergrund hat. //Gleiche Vordergrund- und Hintergrundfarbe einstellen Schriftarten in der Benutzeroberfläche In den meisten Benutzeroberflächen sind Texte ein wesentlicher Informationsträger. In Java kann die Klasse Font als Teil der Klasse java.awt dazu verwendet werden, die benötigte Schriftart (sog. Font) und deren Eigenschaften einzustellen. Ein Font wird immer mit Name, Stil und Größe in Punkten spezifiziert. Zur Erzeugung eines Font-Objekts muss daher der Name des Fonts, dessen Stil und die Punktgröße an den Konstruktor übergeben werden. Beispiele für Font-Namen sind Times New Roman oder Arial. Stile sind bspw. Normal, Fett oder Kursiv. Es ist daher logisch, dass Bestandteile der Klasse Font die Kombinationen Font.PLAIN (normal), Font.BOLD (fett) und Font.ITALIC (kursiv) sind. Im folgenden Beispiel wird ein Font erzeugt, der eine kursive fettgedruckte Serifenschrift der Größe 10 Punkt verwendet.
//Kursive fettgedruckte Serifenschrift 10 Punkt Nachdem ein Font initialisiert worden ist, kann er mit der Methode setFont, die Teil der Klasse Graphics ist, einer Bildschirmdarstellung zugewiesen werden:
// Setzen des Fonts Meist erfolgen diese Anweisungen, bevor ein Text auch tatsächlich ausgegeben wird. Die Textausgabe erfolgt in einem Applet mit der Methode drawString als Teil der Klasse Graphics (wenn der Text nicht Teil einer anderen Komponente, wie bspw. eines Knopfs ist). drawString erwartet als Argumente einen String und die Position, an der der Text ausgegeben werden soll. Hierbei gibt die x-Koordinate die linke Begrenzung des Textes an und die y-Koordinate die Basislinie, auf der der Text aufsetzt. Zusammengesetzt sieht das Beispiel wie folgt aus:
//Vollstaendige Textausgabe Font f = new Font("Serif", Font.ITALIC + Font.BOLD, 10); } Es kommt ab und an vor, dass man eine Font-Größe wählt, die nicht verfügbar ist. Um dieses Problem zu vermeiden, kann die Klasse FontMetrics verwendet werden, die Informationen über einen Font zur Verfügung stellt. Hierzu muss zuerst mittels getFontMetrics ein Objekt erzeugt werden. getFontMetrics akzeptiert ein Argument: Ein Objekt vom Typ Font. Anschließend können die folgenden Methoden aufgerufen werden:
Das folgende Beispiel illustriert die Verwendung dieser Methoden. int h, sw; } Grafikoperationen in Java2D Seit der Version 1.2 von Java stehen eine Menge erweiternder Routinen zur Verfügung, mit denen hochqualitative Grafiken und Texte erzeugt werden können. Dies sind bspw. verschiedene Füllmuster oder Linienarten. Weiterhin wurde das Konzept des Koordinatensystems signifikant erweitert. Man unterscheidet nun zwischen einem globalen Koordinatensystem, das dem bisher verwendeten System entspricht und einem benutzerdefinierten System, das frei wählbar ebenfalls mit x- und y-Koordinaten arbeitet. Beide Arten sind allerdings nach wie vor kartesische Systeme; affine Systeme können nicht verwendet werden. Wird nichts anderes angegeben, so sind beide Koordinatensysteme identisch. Der Benutzer kann allerdings ein eigenes System angeben, in dem die linke obere Ecke nicht länger den Koordinaten (0,0) des globalen Systems entspricht. Auch Drehungen der x- und der y-Achse sind hier möglich. Bisher wurde zur Verwaltung grafischer Operationen ein Graphics-Objekt verwendet. In Java2D wird dieses Konzept um ein Graphics2D-Objekt erweitert, das durch ein Casting eines Graphics-Objekts generiert werden kann und muss, da alle Java2D-Routinen ausschließlich mit Graphics2D-Objekten arbeiten. Auch Graphics2D ist Teil des Packages java.awt. Das folgende Beispiel illustriert dies.
//Java2D Graphics2D screen2D = (Graphics2D) screen; } Anschließend muss die Art und Weise angegeben werden, wie ein Objekt erzeugt werden soll, bspw. die Art der Farbe, der Linienstärke oder auch des Füllmusters. Farben in Java2D Farben werden in Java2D wie bisher auch angegeben, bspw. als
screen2d.setColor(Color.black);. Linien in Java2D Als Erweiterung zum bisherigen Konzept von Linien, die einen Pixel breit sind, kann die Stärke in Java2D frei gewählt werden. Hierzu wird zuerst der Konstruktor BasicStroke aufgerufen, der drei Argumente akzeptiert:
Abb. 4-8 illustriert die verschiedenen Linienarten. Das folgende Beispiel stellt die Erzeugung einer Linie dar, die abgerundet endet und mit weiteren Linien abgerundet verbunden wird. Die Linienstärke ist hierbei 3 Pixel. In der folgenden Anweisung wird die Bildschirmumgebung mittels der Methode
//Festlegung der Linieneigenschaften screen2D.setStroke(stift);
Abb. 4.8: Linienarten in Java2D Nach der Angabe der Linienart, die in Grafiken angewendet werden soll, kann der Entwickler auch das Muster, mit dem Flächen gefüllt werden sollen, näher spezifizieren. Dies kann entweder eine einzige Farbe sein (wie bisher auch), ein Farbverlauf oder eine Textur. Diese Möglichkeiten sind in Abb. 4-9 dargestellt.
Abb. 4.9: Füllmuster in Java2D Um in Java2D ein Füllmuster festzulegen, kann die GradientPoint-Methode verwendet werden, die sechs Argumente erwartet: Koordinaten x1 und y1, bei denen eine Farbe Farbe1 beginnt, und Koordinaten x2 und y2, bei denen der Farbverlauf mit Farbe2 endet. Alle Koordinaten müssen hierbei vom Typ float sein. Zusätzlich kann als siebtes Argument angegeben werden, ob eine Textur (zyklisches Füllen) oder ein Verlauf (azyklisches Füllen) erzeugt werden soll. Hierzu ist entweder true (zyklisches Füllen) oder false (azyklisches Füllen) anzuhängen. Da die Einstellung false aber standardmäßig angenommen wird, kann der siebte Parameter entfallen, wenn Verläufe verwendet werden sollen. Das folgende Beispiel verdeutlicht die Verwendung von GradientPoint, indem der Füllstil auf einen Verlauf von Schwarz nach Weiß eingestellt wird, der bei den Koordinaten (0,0) beginnt und bei (100,50) endet. Zum Setzen dieses Stils wird dann die Methode setPaint verwendet.
//Festlegung der Art des Füllmusters Nachdem Linieneigenschaften und Füllstil festgelegt wurden, kann das Zeichnen von Objekten in Java2D beginnen. Objekte in Java2D werden als geometrische Formen angelegt, die im Package java.awt.geom enthalten sind. Im Gegensatz zum bisherigen Ansatz unterscheidet man in Java2D zwischen der Definition der Objekte und deren Zeichnen oder Füllen. Als Beispiel kann die oben definierte Methode drawrect betrachtet werden. In Java sind hier zwei Abläufe zusammengefasst: Die Definition und das Malen eines Quadrats. In Java2D hingegen muss ein Quadrat in einer eigenen Anweisung definiert werden und dann anschließend als Argument an die Methoden draw oder fill übergeben werden, die das Zeichnen regeln. Zur Definition grafischer Primitive stehen die folgenden Methoden zur Verfügung:
Im folgenden Beispiel wird ein Kreisbogenausschnitt erzeugt, der mit einem geraden Abschluss geschlossen wird. Der Kreis ist durch die Koordinaten (100,100), die Breite 30 und die Höhe 30 bestimmt, der Kreisausschnitt durch die Winkelangabe 90 Grad und die Winkellänge 45 Grad.
//Zeichnen eines Kreisausschnitts Zunächst muss ein GeneralPath-Objekt angelegt werden, das den Pfad von Punkt zu Punkt verwaltet. Hierzu ist die folgende Syntax zu verwenden:
GeneralPath <Name des Pfades> = new generalPath(); Nachdem ein leeres Polygon angelegt wurde, wird der erste Punkt des Linienzugs mit der moveTo-Methode (Argumente: Punktkoordinaten x und y) in das Polygon eingefügt. Alle weiteren Punkte werden mit der Methode lineTo, die dieselben Argumente wie die moveTo-Methode erwartet, in das Polygon eingefügt. Sowohl moveTo als auch lineTo erwarten alle Argumente als float-Werte. Nachdem das Polygon angelegt wurde, kann der Linienzug mit der Methode closePath geschlossen werden. Das folgende Beispiel illustriert die Verwendung der verschiedenen Methoden.
//Erzeugen eines Polygons aus 5 Punkten und Schliessen des Linienzugs Transformationen Das Transformationsattribut des Graphics2D-Kontexts, das als eine Instanz der Klasse AffineTransform definiert ist, kann verändert werden, um Verschiebungen, Drehungen, Skalierungen oder Verzerrungsoperationen grafischer Objekte durchzuführen. Verzerrungsoperationen zeichnen sich dadurch aus, dass eine Gruppe von Linien auch nach Anwendung der anderen Transformationen noch parallel ist. Graphics2D bietet hierzu verschiedene Möglichkeiten an, die durch eine Generierung einer neuen Instanz der Klasse AffineTransform oder durch den Wechsel des Attributs mittels der Methode setTransform charakterisiert sind. Die Klasse AffineTransform definiert die folgenden bereits vorgefertigten Methoden:
Alternativ kann auch eine der Transformationsmethoden verwendet werden, die Teil von Eine
AffineTransform at = new AffineTransform(); Anzeigebegrenzung Jede grafische Form, die mit Java2D erzeugt wird, kann zur Begrenzung der anzuzeigenden Fläche verwendet werden. Definiert man bspw. einen Kreis und positioniert diesen in der Mitte der Anzeigefläche, so wird nur der Teil einer Grafik oder eines Bildes angezeigt, der sich im Bereich der Fläche des Kreises befindet. Dies wird auch als Clipping bezeichnet. Als Teil des Graphics2D-Kontexts kann das Clip-Attribut gesetzt werden, indem die Methode Graphics2D.setClip aufgerufen wird und dieser die Form als Argument übergeben wird, die den anzuzeigenden Bereich festlegt. Der Clipping-Bereich kann anschließend noch verkleinert werden, indem man der clip-Methode eine weitere Fläche übergibt. Es wird dann nur der Bereich angezeigt, in dem sich der aktuelle Clip und die neue Form überschneiden. Im folgenden Beispiel werden zwei Rechtecke verwendet, die sich teilweise überschneiden. Der Clip betrifft dann nur den Bereich, in dem die Rechtecke überlappen.
Rectangle2D r1 = new Rectangle2D.Float(0F, 0F, 100F, 200F); Vordergrundobjekte Java2D bietet die Möglichkeit, Objekte so anzuzeigen, dass sie andere verdecken. Die Verdeckung ist aber nur eine Art und Weise, Objekte in einem Verbund anzuordnen. Die dazu einsetzbare AlphaComposite-Klasse kapselt verschiedene Stile, die bestimmen, wie überlappende Objekte am Bildschirm angezeigt werden. Eine AlphaComposite-Instanz kann weiterhin mittels eines alpha-Werts den Grad der Transparenz angeben. Hierbei ist ein Objekt vollkommen sichtbar, wenn alpha auf 1.0 steht und vollkommen transparent, wenn alpha den Wert 0.0 hat. Der alpha-Wert kann weiterhin auf einen beliebigen Wert zwischen 0.0 und 1.0 eingestellt werden. In Tab. 4-2 sind die Stile, die zur Definition der Überlappungseigenschaften eingesetzt werden können, angegeben. |
|
|
Tab. 4.2: Überlappungsdefinitionen in Java2D Um den in Graphics2D verwendeten Überlappungsstil zu verändern, muss ein AlphaComposite-Objekt erzeugt werden und an die setComposite-Methode übergeben werden. Im folgenden Beispiel wird ein neues AlphaComposite-Objekt aco angelegt, indem AlphaComposite.getInstance aufgerufen wird und der entsprechende Stil gesetzt wird. Soll ein anderer Überlappungsstil oder alpha-Wert gesetzt werden, so wird AlphaComposite.getInstance nochmals aufgerufen und aco das neue AlphaComposite zugewiesen. alpha wird als zweiter Parameter an AlphaComposite.getInstance übergeben.
AlphaComposite aco = AlphaComposite.getInstance(AlphaComposite.SRC); Abbildungsqualität Einer der vielen Vorteile von Java2D ist die skalierbare Darstellungsqualität. In einem Graphics2D-Kontext kann mit einem Attribut angegeben werden, ob Objekte so schnell wie möglich oder aber in einer größtmöglichen Qualität dargestellt werden sollen. Zum Setzen der Darstellungseigenschaften muss ein RenderingHints-Objekt generiert werden und an die Methode setRenderingHints des Graphics2D-Kontexts übergeben werden. Wenn lediglich ein Attribut gesetzt werden soll, kann die Methode setRenderingHint aufgerufen werden, indem ein Paar aus Schlüsselwort und Wert als Argument der zu setzenden Eigenschaft übergeben wird. Die hierzu notwendigen Paare aus Schlüsselwort und Wert sind in der Klasse RenderingHints definiert. Ein Beispiel, in dem die Eigenschaft rendering (Anzeigeart) auf den Wert quality gesetzt wird, ist im Folgenden angegeben. Es muss aber beachtet werden, dass eine Veränderung des Darstellungsmodus nicht von allen Plattformen umgesetzt werden kann. Obwohl daher dieser Wert stets gesetzt werden kann, ist eine Umsetzung nicht in allen Fällen gewährleistet.
screen2.setRenderingHint(RenderingHints.KEY_RENDERING,qu ality); In der Klasse RenderingHints werden die folgenden Eigenschaften unterstützt (ein Setzen auf den Wert default bewirkt, dass die standardmäßig auf einer Plattform vorhandenen Anzeigemöglichkeiten verwendet werden):
Komplexe Grafikformen Mittels geometrischer Formen können durch Überlagerung komplexe Flächen geschaffen werden. Die Disziplin, die sich mit der Erzeugung geometrischer Formen durch Anwendung boole'scher Operationen beschäftigt, wird auch als Constructive Area Geometry (CAG) bezeichnet. In Java2D stehen für derartige Aufgaben sog. Areas (Flächen) zur Verfügung. Eine Area kann aus einer beliebigen geometrischen Form erzeugt werden und unterstützt die folgenden boole'schen Operationen:
Beispiele für derartige Mengen sind in Abb. 4-12 angegeben. Im folgenden Code-Beispiel wird erläutert, wie eine Schnittmenge zweier Kreise generiert werden kann. Die Verwendung der anderen Methoden erfolgt analog. Abb. 4.12: Boole'sche Operationen mit Flächen Bilder in Benutzeroberflächen Zur Verwendung von Bildern steht in Java die Klasse Image zur Verfügung, die Teil des Packages java.awt ist. Zum Laden und Anzeigen von Bildern werden die Methoden der Klassen Graphics und Applet eingesetzt, wenn Applets implementiert werden. Die hierzu notwendigen Mechanismen werden in diesem Unterkapitel beschrieben. Um ein Bild anzeigen zu können, muss dieses zunächst über das World Wide Web in ein Java-Programm eingeladen werden. Es ist daher auch verständlich, dass Bilder separat von Java-Klassendateien gespeichert werden. Die in Java verwendeten Dateiformate sind JPEG (Endung .jpg) oder GIF (Endung .gif) [Stei98]. Zum Laden eines Bildes über das WWW muss eine URL angegeben werden. Hierzu wird die Klasse URL verwendet, die Teil des Packages java.net ist. Mit der folgenden Anweisung kann ein URL-Objekt erzeugt werden, das sich auf das Bild kom.jpg bezieht:
URL url = new URL("http://www.kom.e-technik.tu-darmstadt.de/kom.jpg"); Innerhalb der Applet-Klasse kann ein Bild auf die folgenden zwei Arten geladen werden:
Oftmals ist es unflexibel, eine URL fest im Programm zu verankern. Es existieren daher zwei weitere Mechanismen, die auf die Verwendung einer URL verzichten:
Die Entscheidung, ob entweder Nach dem Laden eines Bildes kann es mittels der Methode drawImage, die Teil der Klasse Graphics ist, angezeigt werden. Hierzu sind die folgenden Parameter zu verwenden:
Die Koordinaten, an denen ein Bild dargestellt wird, werden in ähnlicher Art und Weise verwendet, wie bei der Erzeugung von Rechtecken mit der Methode Der jeweils letzte Parameter, mit dem
Abb. 4.13: Ausgabe des Bild-Applets Das eigentliche Laden erfolgt in der Methode init. In der Methode paint wird die Größe des Bildes abgefragt und diese dann um die Hälfte vergrößert. Die Ausgabe des Applets ist in Abb. 4-13 dargestellt.
import java.applet.Applet; Image img; img = getImage(getCodeBase(), "kom.jpg"); } int breite = img.getWidth(this); } } Animation in Benutzeroberflächen Animationen können in Benutzeroberflächen auf vielfältige Art und Weise verwendet werden bspw., um Textfarben dynamisch zu verändern, um einen Film durch aneinandergereihte Einzelbilder (wobei diese Bilder in einem Array-Objekt gespeichert werden) zu simulieren, aber auch, indem Grafikobjekte ihre Position dynamisch verändern, wodurch wiederum der Eindruck eines ablaufenden Films entsteht. Hierbei tritt allerdings ein großes Problem auf: Die Darstellung derartiger Animationen flackert, wenn keine Ergänzungen des Codes vorgenommen werden. In diesem Abschnitt werden Mechanismen beschrieben, die das Flackern verhindern, speziell die Doppelpufferungsmethode. Die Ursache für Flackereffekte liegt darin, dass zur Anzeige eines neuen Bildes einer Animation die Methode repaint aufgerufen werden muss, die wiederum paint aufruft. repaint ruft allerdings paint nicht direkt auf, sondern vorher die Methode update, die den Inhalt des Bildschirms dadurch löscht, dass der Bildschirm mit der Hintergrundfarbe aufgefüllt wird. Es ist nun klar ersichtlich, dass ein Flimmereffekt kaum vermeidbar ist, wenn zwischen je zwei Bildern einer Animation ein anderes (einfarbiges) Bild eingeschoben wird. Grundsätzlich stehen die folgenden zwei Möglichkeiten zur Verfügung, um diesen Effekt zu verhindern:
Verhinderung des Bildschirmlöschens Standardmäßig sieht die
public void update (Graphics screen){ screen.setColor(getBackground()); } Mittels der Methode getSize wird hierbei die Größe des Bildschirmbereichs des Applets in Erfahrung gebracht. Eine Möglichkeit besteht nun darin, das Löschen des Bildschirms auszulassen. Dies ist allerdings nur dann sinnvoll, wenn sich kein Objekt bewegt. Anderenfalls sind Darstellungsfehler zu erwarten. Die überschriebene Methode ist im Folgenden angegeben. Es muss allerdings darauf hingewiesen werden, dass dieser Ansatz zwar einfach, aber problematisch ist. Es ist daher ratsam, die im Folgenden beschriebene Doppelpufferung zu verwenden.
public void update (Graphics screen){ paint(screen); } Doppelpufferung Bei der Doppelpufferung wird der gesamte anzuzeigende Inhalt in einen nicht sichtbaren Bereich, einen Puffer, geschrieben, der anschließend in den sichtbaren Bildschirmbereich kopiert wird. Der Vorteil der Doppelpufferung besteht vor allem darin, dass die Berechnung des anzuzeigenden Inhalts von der Anzeige entkoppelt wird. Zustände, in denen das Bild erst teilweise berechnet ist, werden daher dem Betrachter nicht präsentiert. Hierdurch ergibt sich speziell bei Animationen ein flüssiger Bewegungsablauf. Die Doppelpufferung ist allerdings mit wesentlichem Aufwand verbunden, da zwei Bildschirmbereiche verwendet werden müssen. Hierdurch wird mehr Rechenaufwand und auch mehr Speicherplatz im Hauptspeicher benötigt. Vorteil der Doppelpufferung ist, dass nahezu sämtliche Flackereffekte ausgeschlossen werden können. Zur Verwendung der Doppelpufferung sind die folgenden vier Schritte notwendig:
Image verborgenesBild; screen.drawImage(verborgenesBild, 0, 0, this); paint(screen); } Im Folgenden ist ein Beispiel angegeben, das die Technik der Doppelpufferung anwendet. Hierzu soll ein roter Kreis jeweils von links nach rechts und dann zurück über den Bildschirm bewegt werden. Die Geschwindigkeit soll hoch genug sein, um den Eindruck einer fortlaufenden Animation zu erzeugen. Hierzu wird eine Variable xpos verwendet, die von Bild zu Bild erhöht bzw. verringert wird. Um das Applet letztendlich zu beenden, wird die Methode destroy verwendet, die den Speicherbereich des im Verborgenen arbeitenden Bildschirmbereichs freigibt. Wie aufwendig die Doppelpufferung tatsächlich ist, wird erkennbar, wenn man den Bildschirmbereich des Applets mit der Maus vergrößert. In diesem Fall läuft das Applet sehr viel langsamer und auch ruckartig. Die Ausgabe des Applets ist in Abb. 4-14 dargestellt.
import java.applet.Applet; Thread t; verborgenesBild = createImage(getSize().width, getSize().height); } if (t == null) { t = new Thread(this); } } t = null; } Thread aktThread = Thread.currentThread(); xpos += xBew; xBew *= -1; repaint(); } catch (InterruptedException e) {} } } public void update(Graphics screen) { paint(screen); } verborgen.setColor(Color.white); } verborgen.dispose(); } }
Abb. 4.14 : Ausgabe des Kugel-Applets Audio in Benutzeroberflächen Ab Java 1.2 stehen eine Reihe von Möglichkeiten zur Verfügung, verschiedene Typen von Audiodateien aus Applets oder Applications heraus abzuspielen. Hierzu zählen die Dateiformate AIFF, AU, WAV, MIDI (Typ 0), MIDI (Typ 1) und RMF. Es können Audiodaten in Mono oder Stereo verarbeitet werden, die mit 8 oder 16 bit aufgenommen wurden, wobei Abtastraten zwischen 8 kHz und 48 kHz verwendet werden. Das Abspielen von Tondateien aus einem Applet heraus wurde in Java 1.2 gegenüber früheren Versionen nicht verändert. Um eine derartige Datei abzuspielen, lädt man einen Audio-Clip mit dem Befehl Applet.getAudioClip und kontrolliert die Wiedergabe mit den Methoden play, loop und stop, die Teil der Klasse AudioClip sind. Um bspw. eine Datei abzuspielen, die im Format WAV gespeichert ist, sind die im Folgenden aufgezählten Schritte nötig. WAV ist ein insbesondere auf PCs häufig verwendetes Audioformat.
AudioClip clip = getAudioClip(getCodeBase(),"beispiel.wav");
Audiodaten werden dann geladen, wenn ein Objekt des Typs Das folgende Beispiel ist eine Anwendung für das Abspielen einer WAV-Audiodatei in Java. Unabhängig vom Typ einer Audiodatei ist der Lade- und Abspielvorgang immer derselbe.
import java.awt.Graphics; AudioClip beispielClip; if (t == null) { t = new Thread(this); } } if (t != null) { if (beispielClip != null) beispielClip.stop(); t = null; } } beispielClip = getAudioClip(getCodeBase(), "beispiel.wav"); } if (beispielClip != null) beispielClip.loop(); Thread dieserThread = Thread.currentThread(); try { Thread.sleep(1000); } catch (InterruptedException e) {} } } } Um die Wartezeit des Benutzers zu verringern, wird die Audiodatei in einem Hintergrund-Thread geladen, anstatt in der init-Methode des Applets. Wenn der Benutzer die Klangwiedergabe startet, bevor die Datei vollständig geladen wurde, kann das Applet so geeignet reagieren. Ab JDK 1.2 können sowohl Applications als auch Applets Audiodateien abspielen. Hierzu wurde dem Package java.applet.Applet eine neue, als static gekennzeichnete Methode hinzugefügt, die es Applications erlaubt, Audio-Clips aus einer URL zu generieren:
public static final AudioClip newAudioClip(URL r) Um nun eine Audiodatei eines Applets abzuspielen, wird die Methode Applet.new-AudioClip verwendet, um die Datei zu laden. Anschließend können wiederum die Methoden play, loop, und stop in bereits bekannter Art und Weise eingesetzt werden. Die folgenden Schritte verdeutlichen den Vorgang:
import java.applet.AudioClip; AudioClip beispielClip; if (t == null) { t = new Thread(this); } } if (t != null) { if (beispielClip != null) beispielClip.stop(); t = null; } } beispielClip = newAudioClip("beispiel.wav"); } if (beispielClip != null) beispielClip.loop(); Thread dieserThread = Thread.currentThread(); try { Thread.sleep(1000); } } AudioPlayer a = new AudioPlayer(); } } Die Audioverarbeitung in Java zeichnet sich durch einen einheitlichen Zugriff auf die Audiomöglichkeiten der zugrunde liegenden Plattform aus. Hierdurch wird es Java-Programmen möglich, Audiodaten aufzunehmen (auch diese zu synthetisieren) und sie wieder abzuspielen. Dienste auf höherer Ebene, wie bspw. die Kompression oder Dekompression, die Synchronisation, das Streaming, Container-Zugriffe (Lesen oder Schreiben) und Netzwerktransporte, werden vom Java Media Framework (JMF) realisiert. JMF ermöglicht es Java-Programmen in einer einheitlichen und einfachen Art und Weise, zeitbasierte Daten, wie bspw. Audio und Video, zu synchronisieren und diese anzuzeigen. Die JMF-Implementierung der Firma Sun verwendet die Java-Sound-Engine, um Audiodaten abzuspielen. Informationen über JMF sind unter der URL http://java.sun.com/products/java-media zu finden. |
|
|