|
Die Grafikausgabe erfolgt in allen Java-Programmen durch Aufruf der Methode paint. Ausnahmen davon sind lediglich Terminalausgaben, die vom Programm durch Aufruf von System.out.println erzeugt werden können. paint wird in abgeleiteten Klassen überlagert und sorgt für die ordnungsgemäße Darstellung der Bildschirmausgabe. Die Methode bekommt ein Objekt vom Typ Graphics übergeben, das einen Grafikkontext darstellt. Dieser stellt die Methoden zur Ausgabe von Text und Grafik zur Verfügung und kann am besten als abstraktes Ausgabegerät aufgefaßt werden. Ein Grafikkontext wird auch dann verwendet, wenn Druckausgaben erzeugt werden sollen oder die Ausgabe in einem Offscreen-Image erfolgt. In unserem Fall ruft paint die beiden Methoden paintBorder und paintField auf:
001 public void paint(Graphics g) 002 { 003 paintBorder(g); 004 paintField(g); 005 } |
An beide Methoden wird der Grafikkontext übergeben, auf dem die Ausgabeoperationen vorgenommen werden. paintBorder ist dafür verantwortlich, den Rahmen um das Spielfeld zu zeichnen:
001 /** 002 * Zeichnet den Rahmen des Spielbretts. 003 */ 004 private void paintBorder(Graphics g) 005 { 006 Insets insets = getInsets(); 007 Dimension size = getSize(); 008 size.height -= insets.top + insets.bottom; 009 size.width -= insets.left + insets.right; 010 fieldsize = new Dimension(); 011 fieldsize.width = (size.width - (2 * bordersize)) / 4; 012 fieldsize.height = (size.height - (2 * bordersize)) / 4; 013 g.setColor(Color.black); 014 g.drawRect( 015 insets.left, 016 insets.top, 017 size.width - 1, 018 size.height - 2 019 ); 020 g.drawRect( 021 insets.left + bordersize, 022 insets.top + bordersize, 023 4 * fieldsize.width, 024 4 * fieldsize.height 025 ); 026 } |
Dazu ermittelt die Methode durch Aufruf von getInsets zunächst die Größe aller Randelemente wie Fensterrahmen, Menüzeile oder Statuszeile. Da das Java-Koordinatensystem in der linken oberen Ecke des zugrundliegenden Fensters beginnt (und nicht in der linken oberen Ecke des Client-Bereichs), müssen diese Werte berücksichtigt werden, damit nicht Teile der Ausgabe hinter den Randelementen verschwinden. Des weiteren ermittelt die Methode durch Aufruf von getSize die Größe des Applet-Fensters, um zusammen mit der als Parameter übergebenen Rahmengröße die Abmessung eines einzelnen Spielsteins zu berechnen. Diese wird der Instanzvariable fieldsize vom Typ Dimension zugewiesen und bei der späteren Anzeige des Spielfelds verwendet.
Anschließend wird die Zeichenfarbe auf schwarz geschaltet und die beiden Rechtecke zur Begrenzung des Rahmens gezeichnet. Bei diesem Rahmen handelt es sich natürlich nicht um die oben erwähnten Rahmenelemente des Java-Fensters, sondern um den Rand, den wir zur Begrenzung des Spielfelds selbst zeichnen. Würde innerhalb von paint lediglich paintBorder aufgerufen, so hätte das Applet das in Abbildung 2.4 gezeigte Aussehen.
Abbildung 2.4: Die Rahmendarstellung
Die Darstellung des Spielfelds erfolgt in der Methode paintField, die ebenfalls den Grafikkontext aus paint übergeben bekommt. paintField bestimmt durch Aufruf von getInsets zunächst die linke obere Ecke des Client-Bereichs und setzt dann die Zeichenfarbe auf schwarz. Anschließend wird das Array aFields zeilenweise durchlaufen und für jede der 16 Positionen auf dem Bildschirm die Nummer des darauf befindlichen Spielsteins ermittelt. Falls es sich um die Nummer 15 handelt, wird mit fillRect ein schwarzes Rechteck in der Größe eines Spielsteins gezeichnet, um die Lücke darzustellen. Andernfalls wird der Spielstein gezeichnet. Dazu geht das Programm in drei Schritten vor:
Hier ist der Quelltext von paintField:
001 /** 002 * Zeichnet die Spielsteine auf dem Brett. 003 */ 004 private void paintField(Graphics g) 005 { 006 int imagenumber, image_i, image_j; 007 Insets insets = getInsets(); 008 Point topleft = new Point(); 009 topleft.x = insets.left + bordersize; 010 topleft.y = insets.top + bordersize; 011 g.setColor(Color.black); 012 for (int i = 0; i <= 3; ++i) { 013 for (int j = 0; j <= 3; ++j) { 014 imagenumber = aFields[i][j]; 015 if (imagenumber == 15) { 016 //Lücke zeichnen 017 g.fillRect( 018 topleft.x + j * fieldsize.width, 019 topleft.y + i * fieldsize.height, 020 fieldsize.width, 021 fieldsize.height 022 ); 023 } else { 024 //Image darstellen 025 image_i = imagenumber / 4; 026 image_j = imagenumber % 4; 027 g.drawImage( 028 image, 029 topleft.x + j * fieldsize.width, 030 topleft.y + i * fieldsize.height, 031 topleft.x+j*fieldsize.width+fieldsize.width, 032 topleft.y+i*fieldsize.height+fieldsize.height, 033 image_j*(imagesize.width/4), 034 image_i*(imagesize.height/4), 035 image_j*(imagesize.width/4)+imagesize.width/4, 036 image_i*(imagesize.height/4)+imagesize.height/4, 037 this 038 ); 039 //Rahmen 040 g.drawRect( 041 topleft.x + j * fieldsize.width, 042 topleft.y + i * fieldsize.height, 043 fieldsize.width, 044 fieldsize.height 045 ); 046 //Beschriftung 047 g.drawString( 048 "" + imagenumber, 049 topleft.x + j * fieldsize.width + 2, 050 topleft.y + i * fieldsize.height + 12 051 ); 052 } 053 } 054 } 055 } |
Mit den beiden bisher beschriebenen Methoden wäre die Grafikausgabe eigentlich schon komplett. Die zusätzlich vorhandene Methode update, die normalerweise nicht überlagert werden muß, dient dazu, das Bildschirmflackern zu vermindern: |
![]() |
|
![]() |
001 public void update(Graphics g) 002 { 003 Image dbImage; 004 Graphics dbGraphics; 005 006 //Double-Buffer initialisieren 007 dbImage = createImage(getSize().width,getSize().height); 008 dbGraphics = dbImage.getGraphics(); 009 //Hintergrund löschen 010 dbGraphics.setColor(getBackground()); 011 dbGraphics.fillRect(0,0,getSize().width,getSize().height); 012 //Vordergrund zeichnen 013 dbGraphics.setColor(getForeground()); 014 paint(dbGraphics); 015 //Offscreen-Image anzeigen 016 g.drawImage(dbImage,0,0,this); 017 dbGraphics.dispose(); 018 } |
Ihre Arbeitsweise beruht auf dem Einsatz eines Offscreen-Images, das zur Zwischenspeicherung der Grafikausgabe verwendet wird. Das Verfahren wird auch als Doppelpufferung bezeichnet, weil ein zweiter Bildschirmpuffer zur Ausgabe verwendet wird. update wird in der Aufrufkette, die bei einem Neuzeichnen des Bildschirms ausgelöst wird, unmittelbar vor paint aufgerufen. Ihre Aufgabe besteht darin, zunächst den Hintergrund mit der aktuellen Hintergrundfarbe zu füllen, dann die Vordergrundfarbe zu aktivieren und schließlich paint aufzurufen.
Genau das erledigt auch unsere Variante von update. Alle Zeichenoperationen spielen sich dabei allerdings in einem separaten Puffer ab, der erst nach kompletter Fertigstellung in den Bildschirm eingeblendet wird. Das unschöne Bildschirmflackern, das dadurch entsteht, daß unmittelbar vor der Darstellung dunkler Flächen die darunterliegende helle Fläche des Hintergrunds neu gezeichnet wird, kann so vermieden werden. Der Nachteil ist die etwas verringerte Ausgabegeschwindigkeit und der recht hohe Speicherbedarf, denn bei jedem Neuzeichnen wird ein neues Offscreen-Image erzeugt.
Weiterführende Informationen zur Grafikausgabe finden sich in Kapitel 14 und den folgenden Kapiteln. Kapitel 14 erläutert dabei insbesondere die grundlegenden Zeichenfunktionen und beschreibt den Grafikkontext. Kapitel 15 erläutert die Textausgabe und Kapitel 16 das Farbsystem von Java. Die zeilenorientierte Ausgabe von einfachen Texten wird in Kapitel 3 erläutert und in Kapitel 12 noch einmal aufgegriffen. In Kapitel 24 werden verschiedene Techniken zur Reduzierung des Bildschirmflackerns vorgestellt. |
![]() |
|
![]() |
|
Go To Java 2, Addison Wesley, Version 1.0.2, © 1999 Guido Krüger, http://www.gkrueger.com |