Bei einer genauen Betrachtung fällt auf, dass ImageIcon eine Implementierung der Schnittstelle Icon ist und dass die JLabel-Klasse ein Icon-Objekt erwartet und nicht speziell ein Argument vom Typ ImageIcon. Das heißt aber: Wir können auch eigene Icon-Objekte zeichnen. Dazu müssen wir nur drei spezielle Methoden von Icon implementieren: die Methode paintIcon(…) und ferner zwei Methoden, die die Dimensionen angeben.
interface interface javax.swing.Icon
- int getIconWidth()
Liefert die feste Breite eines Icons. - int getIconHeight()
Liefert die feste Höhe eines Icons. - void paintIcon(Component c, Graphics g, int x, int y)
Zeichnet das Icon an die angegebene Position. Der Parameter Component wird häufig nicht benutzt. Er kann jedoch eingesetzt werden, wenn weitere Informationen beim Zeichnen bekannt sein müssen, wie etwa die Vorder- und Hintergrundfarbe oder der Zeichensatz.
Die folgende Klasse zeigt die Verwendung der Icon-Schnittstelle. Das eigene Icon soll einen einfachen roten Kreis mit den Ausmaßen 20 × 20 Pixel besitzen:
class CircleIcon implements Icon { @Override public void paintIcon( Component c, Graphics g, int x, int y ) { g.setColor( Color.red ); g.fillOval( x, y, getIconWidth(), getIconHeight() ); } @Override public int getIconWidth() { return 20; } @Override public int getIconHeight() { return 20; } }
Wir überschreiben die drei erforderlichen Methoden, sodass ein Icon-Objekt der Größe 20 × 20 Pixel entsteht. Als Grafik erzeugen wir einen gefüllten roten Kreis. Dieser kann als Stopp-Schaltfläche verwendet werden, ohne dass wir eine spezielle Grafik verwenden müssen. Für die Grafik stehen uns demnach 400 Pixel zur Verfügung – genau getIconWidth() mal getIconHeight() –, und alle nicht gefüllten Punkte liegen transparent auf dem Hintergrund. Dies ist auch typisch für leichtgewichtige Komponenten. Über das Component-Objekt können wir weitere Informationen herausholen, wie etwa den aktuellen Zeichensatz oder die darstellende Vaterkomponente.
Um das eigene Icon auf ein JLabel zu setzen, lässt sich die Icon-Implementierung entweder im Konstruktor übergeben oder später über setIcon(Icon):
JLabel circle = new JLabel( new CircleIcon() );
Was die Typen Icon und Image verbindet
Vielleicht wird der eine oder andere sich schon überlegt haben, ob nun ImageIcon eine ganz eigene Implementierung neben der Image-Klasse ist oder ob beide miteinander verwandt sind. Das Geheimnis ist, dass ImageIcon die Icon-Schnittstelle implementiert, aber auch ImageIcon intern die Image-Klasse nutzt. Sehen wir uns das einmal im Detail an: Ein ImageIcon ist serialisierbar. Also implementiert es die Schnittstelle Serializable. Im Konstruktor kann ein URL-Objekt oder ein String mit einer URL stehen. Hier wird einfach getImage(…) vom Toolkit aufgerufen, um sich eine Referenz auf das Image-Objekt zu holen. Eine geschützte Methode loadImage(Image) wartet nun mithilfe eines MediaTrackers auf das Bild. Nachdem dieser auf das Bild gewartet hat, setzt er die Höhe und Breite, die sich dann über die Icon-Methoden abfragen lassen. Doch ein richtiges Icon muss auch paintIcon(…) implementieren. Hier verbirgt sich nur die drawImage(…)-Methode.
Kommen wir noch einmal auf die Serialisierbarkeit der ImageIcon-Objekte zurück. Die Klasse implementiert dazu die privaten Methoden readObject(…) und writeObject(…). Der Dateiaufbau ist sehr einfach. Breite und Höhe befinden sich im Datenstrom, und anschließend existiert ein Integer-Feld mit den Pixelwerten. In readObject(…) liest s.defaultReadObject() – wobei s der aktuelle ObjectInputStream ist – das Feld wieder ein, und über die Toolkit-Methode createImage(…) wird die Klasse MemoryImageSource genutzt, um das Feld wieder zu einem Image-Objekt zu konvertieren. Umgekehrt ist es genauso einfach. writeObject(…) schreibt die Breite und Höhe und anschließend das Ganzzahl-Feld mit den Farbinformationen, das es über einen PixelGrabber bekommen hat.