Advanced   Java   Services
Canvas  Image  MediaTracker
Back Next Up Home

Wie schon im vorigen Abschnitt gesehen, dient ein Canvas dient zum Entwicklen eigener Komponenten. Die AWT Standardklassen Button oder Label können keine Bilder darstellen. Daher zählt ein Canvas, der Bilder im Format .jpg oder .gif darstellen kann, zu den beliebtesten Beispielen. Durch die Swingklassen JLabel und JButton hat natürlich die Klasse Canvas in dieser Richtung an Bedeutung verloren. Es ist trotzdem eine gute Übung, einen solchen Canvas für Bilder zu entwickeln. Um Canvas sinnvoll einzusetzen, muß man eine Unterklasse von Canvas anlegen und mindestens die Methode public void paint(Graphics g) überschreiben. paint() hat in Canvas folgende Implementierung.

public void paint(Graphics g) { g.setColor(getBackground()); g.fillRect(0, 0, width, height); }

Im einfachsten Fall legen wir im Datenteil eine Referenz für ein Imageobjekt an, das dann über den Konstruktor initialisiert wird. Mit der Methode drawImage() aus der Klasse Graphics wird das Bild dann in paint() dargestellt. Da die Methode nicht prüft, ob das übergebene Objekt nicht null ist, müssen wir das tun.

import java.awt.*; public class SimpleImageCanvas extends Canvas { private Image img ; public SimpleImageCanvas(Image im) { img = im ; } public void paint(Graphics g) { if (img!=null) g.drawImage(img, 0, 0, this) ; } } // end class

Ein einfaches Demoprogramm, das unsere Klasse testet, kann so aussehen.

import java.awt.*; public class SimpleImageCanvasDemo extends Frame { public SimpleImageCanvasDemo() { super("SimpleImageCanvasDemo"); Image im = Toolkit.getDefaultToolkit().getImage("mandrill.jpg"); SimpleImageCanvas sic = new SimpleImageCanvas(im); add(sic); setSize(400,300); setLocation(60,100); setVisible(true); } public static void main(String[] args) { SimpleImageCanvasDemo sc = new SimpleImageCanvasDemo(); } // end main } // end class

Das Programm zeigt auch, wie man mit Hilfe der Klasse Toolkit aus einer Bilddatei ein Bildobjekt vom Typ Image erhält. Dies ist für einen Benutzer unserer Klasse aber umständlich. Deshalb ergänzen wir unsere Klasse um einen Konstruktor, der diese Arbeit erledigt.

import java.awt.*; public class SimpleImageCanvas extends Canvas { private Image img ; public SimpleImageCanvas(Image im) { img = im ; } public SimpleImageCanvas(String fileName) { if(fileName!=null) img = Toolkit.getDefaultToolkit().getImage(fileName); } public void paint(Graphics g) { if (img!=null) g.drawImage(img, 0, 0, this) ; } } // end class

Damit vereinfacht sich unser Testprogramm

import java.awt.*; public class SimpleImageCanvasDemo extends Frame { public SimpleImageCanvasDemo() { super("SimpleImageCanvasDemo"); SimpleImageCanvas sic = new SimpleImageCanvas("mandrill.jpg"); add(sic); setSize(400,300); setLocation(60,100); setVisible(true); } public static void main(String[] args) { SimpleImageCanvasDemo sc = new SimpleImageCanvasDemo(); } // end main } // end class

Als nächstes möchten wir unser Bild zentriert anzeigen. Dazu müssen wir die Größe des Bildes wissen. Zwar gibt es in der Klasse Image die Methoden getHeight() und getWidth(), da aber das Laden eines Bildes vom externen Speicher inn den Hauptspeicher ein längerdauernder Vorgang ist, liefern diese Methoden zunächst -1 . Wir können sie erst benützen, wenn das Bild vollständig geladen ist. Mit der Klasse MediaTracker haben wir die Möglichkeit, abzuwarten und auf das Bild erst dann zuzugreifen, wenn es vollständig geladen ist. Dafür schreiben wir uns die Methode trackImage(). Sie erhält ein Imageobjekt, und erzeugt ein Mediatrackerobjekt. Mit addImage() wird das Bild an den MediaTracker übergeben, waitForAll() wartet, bis das Bild vollständig geladen ist, danach wird die Bildgröße ermittelt. Als Parameter in getWidth() und getHeight() ist vom Typ ImageObserver. Dieses Interface wird von der Klasse Component implementiert. Hier wird die Komponente angegeben, die das Bild darstellt. Um die Bildgröße aufnehmen zu können, haben wir den Datenteil der Klasse entsprechend ausgebaut. Die neue Methode kommt in den Konstruktoren zur Anwendung. Außerdem überprüfen die Konstruktoren nun, ob "vernünftige" Parameter übergeben wurden.

import java.awt.*; public class SimpleImageCanvas extends Canvas { private Image img ; private int imWidth, imHeight; public SimpleImageCanvas(Image im) { if (im!=null) { img = im ; trackImage(img); } } public SimpleImageCanvas(String fileName) { if(fileName!=null) img = Toolkit.getDefaultToolkit().getImage(fileName); if (img!=null) trackImage(img); } private void trackImage(Image im) { MediaTracker mt = new MediaTracker(this); mt.addImage(img,0); try { mt.waitForAll(); imWidth = img.getWidth(this) ; imHeight = img.getHeight(this) ; } catch(InterruptedException ex) { } } public void paint(Graphics g) { int x, y ; Dimension dim = getSize() ; if(img!=null) // Bild zentrieren, falls möglich { x = (dim.width-imWidth)/2 ; y = (dim.height-imHeight)/2 ; if (x<0) x=0 ; if (y<0) y=0; g.drawImage(img,x,y,this); } } } // end class

In paint() wird nun versucht, das Bild zu zentrieren. Falls die Bildbreite kleiner ist als die Breite der Komponente wird in dieser Richtung zentriert, falls nicht wird linksbündig dargestellt. Analog für die vertikale Richtung.

Es wäre schön, wenn wir nachträglich noch ein anderes Bild anzeigen könnten, oder auch ein Bild löschen könnten, ohne ein neues anzuzeigen. Die entsprechenden Methoden sind schnell geschrieben:

// Bild nachträglich einfügen public void setImage(Image im) { if (im!=null) { img = im; trackImage(img); repaint(); } } public void setImage(String file) { if (file!=null) { img = Toolkit.getDefaultToolkit().getImage(file); if (img!=null) { trackImage(img); repaint(); } } } public void deleteImage() { if (img!=null) { img=null; repaint(); } } public Image getImage() { return img; }

Alle set-Methoden verändern nur etwas, wenn nicht null übergeben wird. setImage(String f) überprüft zudem, ob getImage() wirklich etwas liefert für den Fall, daß einDateiname übergeben wird, der keine Bilddatei darstellt. deleteImage() prüft, ob img nicht schon null ist, in diesem Fall ist ja nichts zu tun. getImage() schließlich ist mehr der Vollständigkeit halber geschrieben worden, um ein sauberes objektorientiertes Design zu haben.

Mehr Überlegung fordert der Wunsch, unter dem Bild den Bildtitel zentriert auszugeben. Wir ergänzen im Datenteil der Klasse private String fileName. Der passende Konstruktor kann dieses Datum setzen. Außerdem muß darauf geachtet werden, daß in den anderen Methoden die neue Variable richtig gesetzt wird.

Die Pixelgröße eines Strings kann man mit einem Objekt der Klasse FontMetrics in drei Zeilen ermitteln.

FontMetrics fm = g.getFontMetrics(); int h = fm.getHeight() ; // Pixelhöhe der Schrift int w = fm.stringWidth(fileName) ; // Pixelbreite des Strings

Ein ähnlicher Algorithmus wie bei der Bildzentrierung zentriert den Dateinamen mit 5 Pixel Abstand unter dem Bild, falls die Breite des Dateinamens kleiner als die Bildbreite ist.

import java.awt.*; public class SimpleImageCanvas extends Canvas { Image img ; String fileName; int imWidth, imHeight; public SimpleImageCanvas(Image im) { if (im!=null) { img = im ; trackImage(img); } } public SimpleImageCanvas(String file) { if (file!=null) { fileName = file; img = Toolkit.getDefaultToolkit().getImage(file); if (img!=null) trackImage(img); } } private void trackImage(Image im) { MediaTracker mt = new MediaTracker(this); mt.addImage(img,0); try { mt.waitForAll(); imWidth = img.getWidth(this) ; imHeight = img.getHeight(this) ; } catch(InterruptedException ex) { } } // Bild nachträglich einfügen public void setImage(Image im) { if (im!=null) { img = im; fileName = null; trackImage(img); repaint(); } } public void setImage(String file) { if (file!=null) { fileName = file; img = Toolkit.getDefaultToolkit().getImage(file); if (img!=null) { trackImage(img); repaint(); } } } public void deleteImage() { if (img!=null) { img=null; fileName=null; repaint(); } } public Image getImage() { return img; } public void paint(Graphics g) { int x, y ; Dimension dim = getSize() ; if(img!=null) { // Bild zentrieren, falls möglich x = (dim.width-imWidth)/2 ; y = (dim.height-imHeight)/2 ; if (x<0) x=0 ; if (y<0) y=0; g.drawImage(img,x,y,this); if(fileName!=null) { FontMetrics fm = g.getFontMetrics(); int h = fm.getHeight() ; // Pixelhöhe der Schrift int w = fm.stringWidth(fileName) ; // Pixelbreite des Strings int offset = (imWidth-w)/2 ; if (offset>=0) g.drawString(fileName, x+offset, y+imHeight+h+5); else // Schrift ist breiter als Bild { offset = (dim.width-w)/2 ; if (offset<0) offset=0 ; // Schrift ist breiter als Canvas g.drawString(fileName, offset, y+imHeight+h+5); } } } } public Dimension getPreferredSize() { if ( imWidth>0 && imHeight>0) return new Dimension(imWidth, imHeight) ; return super.getPreferredSize() ; } public Dimension getMinimumSize() { if ( imWidth>0 && imHeight>0) return new Dimension(imWidth, imHeight) ; return super.getMinimumSize() ; } } // end class

Zudem haben wir noch die Methoden getPreferredSize() und getMinimumSize() überschrieben. Die Methode getPreferredSize() wird von LayoutManagern benützt, um die Größe einer Komponente zu ermitteln. Je nach Layout verwendet der Manager beide Maße (FlowLayout), eines der beiden Maße (BorderLayout) oder keines (BorderLayout, GridLayout).

top Back Next Up Home