From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+Die bisher beschriebenen Fähigkeiten und Eigenschaften einer
+Bean wurden vom GUI-Designer automatisch erkannt, weil die Beans bestimmte
+Design-Konventionen eingehalten haben. Der GUI-Designer verwendet
+eine Instanz der Klasse Introspector
+aus dem Paket java.beans,
+um diese Fähigkeiten nach dem Laden der Bean zu bestimmen.
+
+
+Neben getter-/setter-Methoden und Registrierungsmethoden für
+Events können Beans aber noch weitere Eigenschaften besitzen.
+So kann beispielsweise in der Toolbox ein Icon angezeigt werden. Auch
+Methoden, die nicht den getter-/setter-Konventionen entsprechen, können
+bekanntgemacht werden. Zudem ist es möglich, für Nicht-Standard-Eigenschaften
+spezielle Eigenschafteneditoren zur Verfügung zu stellen.
+
+
+Diese Möglichkeiten stehen erst zur Verfügung, wenn man
+die Bean mit einer expliziten BeanInfo-Klasse
+ausstattet. Sie hat denselben Namen wie die Bean, trägt aber
+zusätzlich den Suffix »BeanInfo« am Ende ihres Namens.
+Zudem muss sie im selben Paket liegen wie die Bean-Klasse. Die BeanInfo-Klasse
+muss das Interface BeanInfo
+implementieren oder aus SimpleBeanInfo
+abgeleitet sein (letzteres bietet den Vorteil, dass bereits alle erforderlichen
+Methoden vorhanden sind und bei Bedarf nur noch überlagert werden
+müssen).
+
+
+BeanInfo
+definiert eine Reihe von Methoden, die der GUI-Designer abfragt. Sie
+liefern jeweils ein Array von Descriptor-Objekten, von denen jedes
+Informationen über eine zu veröffentlichende Methode, Eigenschaft
+oder ein Icon zur Verfügung stellt. Wir wollen uns die drei wichigsten
+von ihnen in den nachfolgenden Abschnitten ansehen.
+
+
+Während der GUI-Designer die ihm bekannten Beans lädt, sucht
+er für jede von ihnen zunächst nach einer passenden BeanInfo-Klasse.
+Findet er eine solche, wird sie verwendet, und der Low-Level-Introspectionsvorgang
+wird gar nicht erst angestoßen. Daraus folgt insbesondere, dass
+in diesem Fall nur die in der BeanInfo-Klasse angegebenen Merkmale
+zur Verfügung stehen.
+
+
+Ist es beispielsweise nötig, eine BeanInfo-Klasse zur Verfügung
+zu stellen, weil dem Designer eine besondere Methode bekanntgemacht
+werden soll, die nicht den getter-/setter-Konventionen entspricht,
+muss die BeanInfo-Klasse auch alle anderen Eigenschaften der Bean
+explizit bekanntmachen. Andernfalls würde der GUI-Designer nur
+die spezielle Methode sehen, alle anderen Merkmale aber ignorieren.
+
+Der GUI-Designer ruft getIcon
+auf, um herauszufinden, ob die Bean ein Icon zur Darstellung in der
+Toolbox zur Verfügung stellen kann. Als Parameter wird dabei
+eine der symbolischen Konstanten ICON_COLOR_16x16,
+ICON_MONO_16x16,
+ICON_COLOR_32x32
+oder ICON_MONO_32x32
+übergeben. Kann die BeanInfo-Klasse ein Icon in der gewünschten
+Form zur Verfügung stellen, muss sie ein passendes Image-Objekt
+erzeugen und an den Aufrufer zurückgeben. Ist das nicht der Fall,
+sollte null
+zurückgegeben werden.
+
+
+Ist die BeanInfo-Klasse aus SimpleBeanInfo
+abgeleitet, ist das Erzeugen eines Image-Objekts
+einfach. SimpleBeanInfo
+besitzt eine Methode loadImage,
+die bei Übergabe eines Dateinamens die ensprechende Bilddatei
+lädt und daraus ein Image
+erzeugt.
+
+Die Methode getPropertyDescriptors
+wird aufgerufen, um ein Array von PropertyDescriptor-Objekten
+zur Verfügung zu stellen. Es enthält für jede öffentliche
+Eigenschaft der Bean ein Element, das dessen Merkmale beschreibt.
+Ein PropertyDescriptor
+kann recht einfach instanziert werden:
+
+
+Lediglich der Name der Eigenschaft und das Klassenobjekt der zugehörigen
+Bean sind anzugeben. Weitere Merkmale können durch Methodenaufrufe
+hinzugefügt werden. Uns interessiert an dieser Stelle lediglich
+die Methode setPropertyEditorClass,
+auf die wir in Abschnitt 44.6.2
+zurückkommen werden.
+
+
+Für jedes von getPropertyDescriptors
+zurückgegebene Element erwartet der GUI-Designer eine setter-
+und eine getter-Methode, deren Signatur dem bekannten Schema entsprechen
+muss.
+
+Zusätzliche Methoden können dem GUI-Designer durch Überlagern
+von getMethodDescriptors
+bekanntgemacht werden. Zurückgegeben wird ein Array, das für
+jede Methode ein Objekt des Typs MethodDescriptor
+enthält. Darin wird im wesentlichen ein Method-Objekt
+gekapselt (siehe Abschnitt 43.3):
+
+
+Nach diesen Vorbemerkungen können wir eine geeignete BeanInfo-Klasse
+für die LightBulb-Komponente
+entwickeln:
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 44 - Beans
+
+
+
+
+
+44.6 BeanInfo-Klassen und Property-Editoren
+
+
+
+
+
+
+
+44.6.1 BeanInfo-Klassen
+
+
+
+
+
+
+
+
+
+
+
+![]()
+
+![]()
+
+
+
+![]()
+
+
+
+
+
+ Warnung
+
+
getIcon
+
+
+
+
+
+
+
+
+
+public Image getIcon(int iconKind)
+
+
+
+java.beans.BeanInfo
+
+
+
+
+
+
+
+
+
+
+![]()
+
+![]()
+
+
+
+![]()
+
+
+
+
+
+ Tipp
+
+
getPropertyDescriptors
+
+
+
+
+
+
+
+
+
+public PropertyDescriptor[] getPropertyDescriptors()
+
+
+
+java.beans.BeanInfo
+
+
+
+
+
+
+
+
+
+public PropertyDescriptor(String propertyName, Class beanClass)
+ throws IntrospectionException
+
+
+
+java.beans.PropertyDescriptor
+
+
+
+
+
+
+
+
+
+
+![]()
+
+
+
+![]()
+
+
+
+
+
+ Hinweis
+
+
getMethodDescriptors
+
+
+
+
+
+
+
+
+
+public MethodDescriptor[] getMethodDescriptors()
+
+
+
+java.beans.BeanInfo
+
+
+
+
+
+
+
+
+
+
+
+
+public MethodDescriptor(Method method)
+
+
+
+java.beans.MethodDescriptor
+Die Klasse LightBulbBeanInfo
+
+
+
+
+Listing 44.10: Die Klasse LightBulbBeanInfo
+
+
+
+
+
+001 /* LightBulbBeanInfo.java */
+002
+003 import java.awt.*;
+004 import java.beans.*;
+005 import java.lang.reflect.*;
+006
+007 public class LightBulbBeanInfo
+008 extends SimpleBeanInfo
+009 {
+010 public Image getIcon(int iconKind)
+011 {
+012 String imgname = "bulbico16.gif";
+013 if (iconKind == BeanInfo.ICON_MONO_32x32 ||
+014 iconKind == BeanInfo.ICON_COLOR_32x32) {
+015 imgname = "bulbico32.gif";
+016 }
+017 return loadImage(imgname);
+018 }
+019
+020 public PropertyDescriptor[] getPropertyDescriptors()
+021 {
+022 try {
+023 PropertyDescriptor pd1 = new PropertyDescriptor(
+024 "lightOn",
+025 LightBulb.class
+026 );
+027 //pd1.setPropertyEditorClass(LightBulbLightOnEditor1.class);
+028 PropertyDescriptor[] ret = {pd1};
+029 return ret;
+030 } catch (IntrospectionException e) {
+031 System.err.println(e.toString());
+032 return null;
+033 }
+034 }
+035
+036 public MethodDescriptor[] getMethodDescriptors()
+037 {
+038 MethodDescriptor[] ret = null;
+039 try {
+040 Class bulbclass = LightBulb.class;
+041 Method meth1 = bulbclass.getMethod("toggleLight", null);
+042 ret = new MethodDescriptor[1];
+043 ret[0] = new MethodDescriptor(meth1);
+044 } catch (NoSuchMethodException e) {
+045 //ret bleibt null
+046 }
+047 return ret;
+048 }
+049 }
+
+
+LightBulbBeanInfo.java
+
+Wird die Klasse übersetzt und in die jar-Datei für die Beanbox +aufgenommen, verändert sich die Präsentation der LighBulb-Bean +in der Beanbox: +
+Während die ersten beiden Veränderungen nach dem Starten +der Beanbox offensichtlich sind, können wir die letzte überprüfen, +indem wir den Aufruf von toggleLight +auf das Ereignis einer anderen Bean legen. Dazu platzieren wir im +GUI-Designer zunächst eine LightBulb- +und eine LightedPushButton-Bean. +Anschließend markieren wir die LightedPushButton-Bean +und rufen den Menüpunkt »Edit.Events.PropertyChange.propertyChange« +auf. Die rote Verbindungslinie ziehen wir auf die LightBulb +und fixieren sie dort mit einem Mausklick. + +
+Aus der Auswahlliste der verfügbaren parameterlosen Methoden +können wir nun toggleLight +auswählen und »OK« drücken. Die Beanbox generiert +und übersetzt nun eine Adapterklasse, die bei jedem PropertyChangeEvent +des LightedPushButton die Methode +toggleLight der LightBulb +aufruft. Nach jedem Drücken des Buttons verändert die Lampe +also ihren Zustand. Die Lampe kann natürlich nach wie vor auch +im Eigenschaftenfenster an- und ausgeschaltet werden. + + + + +
+Die Beanbox und andere GUI-Designer stellen für einfache Eigenschaften +vordefinierte Editoren zur Verfügung, mit denen ihr Wert verändert +werden kann. Bereits bei indizierten Eigenschaften muss die Beanbox +aber passen. Auch für Objekttypen kann ein GUI-Designer keinen +Standard-Editor zur Verfügung stellen, weil er die Konfigurationsschnittstelle +des Objekts nicht kennt. Für diesen Zweck bietet die Beans-Architektur +die Möglichkeit, eigene Editoren zu definieren und bestimmten +Eigenschaften von Beans zuzuordnen. + +
+In Zeile 027 von Listing 44.10 +ist der Aufruf von setPropertyEditorClass +auskommentiert, damit die Beanbox den eingebauten Editor für +boolesche Werte verwendet. Durch Entfernen des Kommentars erhält +der PropertyDescriptor +der Eigenschaft »lightOn« die Information, dass zum Editieren +dieser Eigenschaft ein benutzerdefinierter Editor verwendet werden +soll. + +
+Benutzerdefinierte Eigenschafteneditoren werden üblicherweise +aus der Klasse PropertyEditorSupport +des Pakets java.beans +abgeleitet. Sie besitzt eine Reihe von Methoden, die in eigenen Editoren +überlagert werden: +
+
+
++public void setValue(Object value) +public Object getValue() + +public String getAsText() + +public void setAsText(String text) + throws java.lang.IllegalArgumentException + +public String[] getTags() + +public boolean isPaintable() + +public boolean supportsCustomEditor() + +public void paintValue(Graphics g, Rectangle box) + +public Component getCustomEditor() ++ + |
++java.beans.PropertyEditorSupport | +
+In den nächsten Abschnitten werden wir drei Editoren für +die Eigenschaft »lightOn« vorstellen. Sie machen in unterschiedlicher +Weise von diesen Methoden Gebrauch. +
+
![]() |
+
+
+ +Eine weitere Variante zur Konfiguration von Beans (auf die wir hier +nicht näher eingehen wollen) ist die Implementierung eines Customizers. +Dieser erhält volle Kontrolle über die Konfiguration der +Bean. Er kann insbesondere auch mehrere Eigenschaften gleichzeitig +verändern und auf diese Weise aufwändige Editoren zur Verfügung +stellen, die den bekannten Wizards oder Assistenten +moderner Entwicklungssysteme ähneln. |
+
+
|
+![]() |
+
+Der erste Editor ist sehr einfach aufgebaut. Er stellt ein Textfeld +zur Verfügung, in dem die Werte »an« und »aus« +eingegeben werden können, um den Zustand der Lampe umzuschalten. + +
+Jeder der nachfolgend vorgestellten Editoren kapselt eine boolesche +Membervariable currentvalue, +die den aktuellen Zustand der Lampe festhält. Sie wird vom GUI-Designer +durch Aufruf von getValue +abgefragt und durch setValue +gesetzt. Beide Methoden operieren mit Objekttypen, d.h. es ist jeweils +eine Konvertierung zwischen boolean +und Boolean +erforderlich. In unserem Beispiel werden zusätzlich die Methoden +getAsText +und setAsText +überlagert, um auf die textuelle Repräsentation (»an« +und »aus«) des booleschen Werts zuzugreifen. + + +
+
+
+
+001 /* LightBulbLightOnEditor1.java */
+002
+003 import java.awt.*;
+004 import java.beans.*;
+005
+006 public class LightBulbLightOnEditor1
+007 extends PropertyEditorSupport
+008 {
+009 boolean currentvalue;
+010
+011 public void setValue(Object value)
+012 {
+013 currentvalue = ((Boolean)value).booleanValue();
+014 }
+015
+016 public Object getValue()
+017 {
+018 return new Boolean(currentvalue);
+019 }
+020
+021 public String getAsText()
+022 {
+023 return "" + (currentvalue ? "an" : "aus");
+024 }
+025
+026 public void setAsText(String text)
+027 throws java.lang.IllegalArgumentException
+028 {
+029 if (text.equalsIgnoreCase("an")) {
+030 currentvalue = true;
+031 } else if (text.equalsIgnoreCase("aus")) {
+032 currentvalue = false;
+033 } else {
+034 throw new IllegalArgumentException(text);
+035 }
+036 firePropertyChange();
+037 }
+038 }
+
+ |
++LightBulbLightOnEditor1.java | +
+Der erste Editor sieht in der Beanbox so aus: +
+ +
+Abbildung 44.8: LightBulbLightOnEditor1 in der Beanbox
+ + + + ++Ist der Wertevorrat für die zu editierende Eigenschaft endlich, +kann es bequemer sein, dem Anwender alle möglichen Varianten +in einer Combox anzubieten. Dazu muss lediglich die Methode getTags +überlagert und ein Array von Strings mit den möglichen Werten +zurückgegeben werden: + + +
+
+
+
+001 /* LightBulbLightOnEditor2.java */
+002
+003 import java.awt.*;
+004 import java.beans.*;
+005
+006 public class LightBulbLightOnEditor2
+007 extends PropertyEditorSupport
+008 {
+009 boolean currentvalue;
+010
+011 public void setValue(Object value)
+012 {
+013 currentvalue = ((Boolean)value).booleanValue();
+014 }
+015
+016 public Object getValue()
+017 {
+018 return new Boolean(currentvalue);
+019 }
+020
+021 public String getAsText()
+022 {
+023 return "" + (currentvalue ? "an" : "aus");
+024 }
+025
+026 public void setAsText(String text)
+027 throws java.lang.IllegalArgumentException
+028 {
+029 System.out.println("setAsText(" + text + ")");
+030 if (text.equalsIgnoreCase("an")) {
+031 currentvalue = true;
+032 } else if (text.equalsIgnoreCase("aus")) {
+033 currentvalue = false;
+034 } else {
+035 throw new IllegalArgumentException(text);
+036 }
+037 firePropertyChange();
+038 }
+039
+040 public String[] getTags()
+041 {
+042 return new String[]{"aus", "an"};
+043 }
+044 }
+
+ |
++LightBulbLightOnEditor2.java | +
+Der zweite Editor sieht in der Beanbox so aus: +
+ +
+Abbildung 44.9: LightBulbLightOnEditor2 in der Beanbox
+ + + + ++Zusätzlich zu den beiden einfachen Varianten kann die Bean aber +auch einen vollkommen frei definierten Editor zur Verfügung stellen. +Sie ist in diesem Fall sowohl für die Darstellung der Eigenschaft +in der Eigenschaftenliste als auch für das Verändern des +aktuellen Wertes mit Hilfe einer eigenen Komponente verantwortlich. +Dazu werden weitere Methoden überlagert: +
+Die folgende Klasse LightBulbLightOnEditor3 +stellt einen sehr einfachen Editor zur Verfügung. Er besteht +lediglich aus zwei nebeneinanderliegenden Rechtecken in den Farben +Blau und Gelb. Die blaue Farbe symbolisiert den ausgeschalteten Zustand, +die gelbe den eingeschalteten. Der aktuelle Zustand der Lampe wird +durch einen schwarzen Rahmen um eines der beiden Rechtecke angezeigt. +Durch einfaches Anklicken eines der beiden Farbfelder kann der Zustand +umgeschaltet werden. + + +
+
+
+
+001 /* LightBulbLightOnEditor3.java */
+002
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import java.beans.*;
+006
+007 public class LightBulbLightOnEditor3
+008 extends PropertyEditorSupport
+009 {
+010 boolean currentvalue;
+011
+012 public void setValue(Object value)
+013 {
+014 currentvalue = ((Boolean)value).booleanValue();
+015 }
+016
+017 public Object getValue()
+018 {
+019 return new Boolean(currentvalue);
+020 }
+021
+022 public boolean isPaintable()
+023 {
+024 return true;
+025 }
+026
+027 public boolean supportsCustomEditor()
+028 {
+029 return true;
+030 }
+031
+032 public Component getCustomEditor()
+033 {
+034 return new LightOnCustomEditor();
+035 }
+036
+037 public void paintValue(Graphics g, Rectangle box)
+038 {
+039 //Linke Box: blau, Lampe ausgeschaltet
+040 g.setColor(Color.blue);
+041 g.fillRect(box.x, box.y, box.width / 2, box.height);
+042 //Rechte Box: blau, Lampe angeschaltet
+043 g.setColor(Color.yellow);
+044 g.fillRect(box.x + box.width / 2, box.y, box.width / 2, box.height);
+045 //Rahmen
+046 g.setColor(Color.black);
+047 for (int i = 0; i < 2; ++i) {
+048 g.drawRect(
+049 box.x + (currentvalue ? box.width / 2 : 0) + i,
+050 box.y + i,
+051 box.width / 2 - 1 - (2 * i),
+052 box.height - 1 - (2 * i)
+053 );
+054 }
+055 }
+056
+057 //---Private Klassen----------------------------------------
+058 class LightOnCustomEditor
+059 extends Canvas
+060 {
+061 public LightOnCustomEditor()
+062 {
+063 addMouseListener(
+064 new MouseAdapter() {
+065 public void mouseClicked(MouseEvent event)
+066 {
+067 currentvalue = (event.getX() > getSize().width / 2);
+068 LightBulbLightOnEditor3.this.firePropertyChange();
+069 repaint();
+070 }
+071 }
+072 );
+073 }
+074
+075 public void paint(Graphics g)
+076 {
+077 paintValue(g, new Rectangle(0, 0, getSize().width, getSize().height));
+078 }
+079
+080 public Dimension getPreferredSize()
+081 {
+082 return new Dimension(120, 60);
+083 }
+084 }
+085 }
+
+ |
++LightBulbLightOnEditor3.java | +
+Der Editor wird durch die aus Canvas +abgeleitete lokale Klasse LightOnCustomEditor +implementiert. Ihre bevorzugte Größe ist fest eingestellt, +und sie verwendet die paintValue-Methode +der umgebenden Klasse zur Bildschirmdarstellung. Bei einem Mausklick +verändert sie deren Membervariable currentvalue +und teilt dies dem GUI-Designer durch Aufruf von firePropertyChange +mit. Anschließend ruft sie repaint +auf, um den Rahmen neu zu zeichnen. + +
+Der dritte Editor sieht in der Eigenschaftenliste so aus: +
+ +
+Abbildung 44.10: LightBulbLightOnEditor3 in der Beanbox
+ ++Wird er angeklickt, erzeugt die Beanbox eine vergrößerte +Variante, in der der Wert verändert werden kann: +
+ +
+Abbildung 44.11: LightBulbLightOnEditor3 in der Eigenschaftenliste
+| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |