inhoudstafel en auteursrecht
* STER *
Een component is een zelfstandige, herbruikbare programma-eenheid die visueel kan geïntegreerd worden in een compositie met behulp van een visueel systeem voor de ontwikkeling van toepassingen.
In het component/containermodel van software-ontwikkeling zijn er twee verschillende rollen voor de programmeurs. Enerzijds heb je de bouwers van componenten, anderzijds de toepassingsprogrammeurs die bestaande componenten verwerken tot grotere, praktisch bruikbare gehelen. Het grotere geheel wordt dan een container genoemd.
Voorbeeld: een elektronische agenda zou kunnen opgebouwd zijn met behulp van een kalender en een database-component.
Bij software-ontwikkeling in Java kunnen de containers zelfstandige toepassingen zijn, maar ook applets, servlets en zelfs andere componenten, zodat een vertakte hiërarchie ontstaat.
Het standaard Java-paradigma voor component based development heet JavaBeans. Technisch is een Bean eender welke klasse. De bruikbaarheid van een Bean als component hangt af van de mate waarin bepaalde conventies worden gehanteerd bij de naamgeving en de werking van de publieke methoden van die klasse.
Meestal wordt van een JavaBean-klasse aangenomen dat ze de interface
java.io.Serializable implementeert, al staat dit strikt genomen
niet in de technische specificatie. Overigens is dit een erg
eenvoudige interface om te implementeren: hij bevat namelijk
geen enkele methode. Als een klasse de interface java.io.Serializable
implementeert, dan kunnen objecten van die klasse in een gecodeerd
formaat worden verstuurd via netwerken of weggeschreven naar een
serieel bestand.
Voor het succesvol gebruik van componenten is het bijzonder belangrijk dat de samenwerking tussen componenten en container nauwkeurig gedefinieerd wordt in een goed afgelijnd raakvlak.
Een JavaBean-component biedt publieke methoden aan. Hij is eveneens in staat bepaalde gebeurtenissen af te handelen, en andere gebeurtenissen zelf te veroorzaken. Een component heeft geen publieke attributen.
Niet alleen de container moet op de hoogte zijn van de diensten die de Bean aanbiedt, maar ook de ontwikkelingsomgeving. De ontwikkelaar van een Bean moet dus eigenlijk twee raakvlakken definiëren: één met de eigenlijke toepassing, en één met de visuele omgeving waarin de toepassing gebouwd wordt.
De ontwikkelingsomgeving achterhaalt de diensten van een Bean op twee manieren:
BeanInfo.Zoals gezegd zijn bij het component/containermodel voor software-ontwikkeling twee verschillende rollen voor de ontwikkelaars: de makers van nieuwe componenten resp. degenen die de componenten integreren in een groter geheel. Deze tekst gaat vooral over de eerste rol.
Sun levert een gratis ontwikkelingsomgeving voor het testen van Beans: de Beans Development Kit (BDK). Momenteel (april 2001) is de meest recente versie BDK1.1. Voor het eigenlijke programmeren van de Beans is geen speciale omgeving nodig, bijvoorbeeld de gewone Java Development Kit volstaat.
Het belangrijkste hulpmiddel in de BDK is de BeanBox. Dat is een primitieve simulatie van een ontwikkelingsomgeving waar JavaBeans kunnen geïntegreerd worden in een eenvoudige container (een formulier). De BeanBox kan bijvoorbeeld automatisch een applet genereren die de gevraagde componenten bevat.
Na installatie van de BDK kan de BeanBox bijvoorbeeld als volgt gestart worden.
c:\bdk1.1\beanbox (of een andere map, naargelang
waar de Bean Development Kit geïnstalleerd is), bijvoorbeeld door de typen
cd c:\bdk1.1\beanboxrun
Enkele belangrijke functies van de BeanBox zijn:
Gebruik een teksteditor (bijvoorbeeld Notepad/Kladblok) om de volgende klassedefinitie
in te voeren in een bestand met de naam RoodVlak.java.
import java.awt.*;
import java.io.Serializable;
public class RoodVlak
extends Canvas implements Serializable {
public RoodVlak() {
setSize(60, 40);
setBackground(Color.red);
}
}
Het enige wat een object van het type RoodVlak
onderscheidt van een gewone rode rechthoek is het feit dat
we de interface Serializable implementeren.
Compileer het bestand (zoals gewoonlijk, bijvoorbeeld met
javac RoodVlak.java om
een binair klassenbestand RoodVlak.class te verkrijgen.
Alvorens een JavaBean in de BeanBox op te nemen, moeten we hem
verpakken in een archief met behulp van het programma
jar (Java archive), dat we
bespraken in hoofdstuk 5b. Dit programma
maakt gewoon deel uit van de JDK. Als onderdeel van het archief moeten
we ook een manifestbestand opnemen. Dat manifestbestand,
dat bijvoorbeeld manifest.txt kan heten, heeft de
volgende inhoud:
Name: RoodVlak.class Java-Bean: True
Nauwkeurig ! En met een regeleinde na de laatste regel. Let
op de hoofdletter T van True
Creëer een archiefbestand RoodVlak.java met de opdracht
jar cfm RoodVlak.jar manifest.txt RoodVlak.class
Open het archiefbestand in de BeanBox met het menu-commando File/LoadJar... Nu verschijnt de nieuwe JavaBean RoodVlak onderaan in het lijstje van de ToolBox.
Je kan een JavaBean uit de ToolBox inlassen in de container van de BeanBox door het desbetreffende item te selecteren in de ToolBox, en vervolgens een rechthoek te slepen in de BeanBox. Het resultaat ziet er ongeveer als volgt uit.
Experimenteer met de verschillende types Bean die in de ToolBox beschikbaar zijn.
Het venster "Properties" (eigenschappen) verandert van uitzicht naargelang van de component die op elk moment geselecteerd is. Ook de container als geheel kan worden geselecteerd door met de muis buiten het domein van de componenten te klikken.
Eigenschappen kunnen worden gewijzigd door erop te klikken in het Properties-venster. Soms, zoals bij een tekstvak, kan je dan gewoon een waarde invullen; soms, zoals bij kleuren, komt een aparte dialoog tevoorschijn om een nieuwe waarde van de eigenschap te specificeren:
Je kan een nieuwe eigenschap aan een Beantype toekennen door de creatie van twee methoden die aan bepaalde vormvereisten voldoen:
get en de andere met het
woord set begint;void; de
getter-methode geen enkele parameter, en als
terugkeertype het parametertype van de settermethode.
In tegenstelling tot wat de benaming 'eigenschap' doet vermoeden,
is het dus niet nodig een attribuut te declareren. Natuurlijk
zullen eigenschappen in de praktijk vaak geïmplementeerd
worden door een private attribuut, dat door de
twee methoden respectievelijk geraadpleegd en ingevuld wordt.
We geven aan bovenstaande Bean RoodVlak een extra
eigenschap, Tekst. Daartoe declareren we een
attribuut
private String woorden = "";
en twee methoden
public void setTekst(String t) {
woorden = t;
}
public String getTekst() {
return woorden;
}
Op die manier kunnen eigenschappen worden gecreëerd van de types
String, Color, Font en int. Voor andere
(object)types moet een aparte editor worden gedefinieerd - zie verder onder
'aanpassing'. Overigens zijn de 'standaard' zichtbare eigenschappen (font, name,
foreground en background) gewoon afkomstig van de publieke methoden van de
klasse Component, geërfd door de klasse Canvas...
Wil je in bovenstaande Bean de eigenschap tekst ook echt
zichtbaar maken in de Beanrechthoek zelf, dan moet je de methode paint
herdefiniëren:
public void paint(Graphics g) {
g.drawString(woorden, 5, 35);
}
In dat geval verdient het tevens aanbeveling, op het einde van
de setTekst-methode de opdracht
repaint();
op te nemen.
Het nu volgende voorbeeld illustreert dat de eigenschappen die de Bean in de BeanBox laat zien, niet noodzakelijk overeenkomen met de 'zichtbare' eigenschappen van de componenten in de draaiende toepassing.
We beginnen met een Bean zonder speciale eigenschappen. Het betreft een paneel met enkele tekstvelden en een drukknop, om de conversie van geldbedragen van Euro naar Frank om te rekenen.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Convertor extends JPanel
implements java.io.Serializable, ActionListener {
JButton btnConverteer = new JButton("Converteer");
JLabel lblOorsprong = new JLabel("Euro");
JLabel lblDoel = new JLabel("Frank");
JTextField txtOorsprong = new JTextField(15);
JTextField txtDoel = new JTextField(15);
public Convertor() {
setLayout(new GridLayout(3, 2, 10, 10));
add(txtOorsprong);
add(lblOorsprong);
add(btnConverteer);
btnConverteer.addActionListener(this);
add(new JPanel()); // opvulsel
add(txtDoel);
add(lblDoel);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(btnConverteer)) {
try {
double bedragOorsprong = Double.parseDouble(txtOorsprong.getText());
double bedragDoel = bedragOorsprong * 40.3399;
txtDoel.setText("" + bedragDoel);
}
catch (NumberFormatException exc) {
txtDoel.setText("Error");
}
}
}
}
Na compilatie, verwerking tot archiefbestand en laden in de BeanBox geeft dit inderdaad een werkende Eurocalculator:
We zullen nu vier aspecten van deze Bean "flexibiliseren", zodat ze door een toepassingsprogrammeur eenvoudig kunnen worden aangepast:
We doen dit door het scheppen van vier eigenschappen: oorsprong, doel, knoptekst en factor.
public void setOorsprong(String naam) {
//...
}
public String getOorsprong() {
//...
}
public void setDoel(String naam) {
//...
}
public String getDoel() {
//...
}
public void setKnoptekst(String tekst) {
//...
}
public String getKnoptekst() {
//...
}
public void setFactor(double f) {
//...
}
public double getFactor() {
//...
}
Implementeer bovenstaande acht methoden. Voeg de nodige private
attributen toe. Pas de bestaande code aan zodat ze gebruik maakt van de nieuwe
eigenschappen. Importeer de nieuwe Bean in de BeanBox en maak er een
duim/centimeter-convertor van door enkele simpele ingrepen in het
venster Properties. (1" = 2.51 cm)
Een eigenschap heet gebonden als eventuele wijzigingen
in de eigenschap automatisch aanleiding geven tot een gebeurtenis.
Dergelijke gebeurtenissen zijn van het type
PropertyChangeEvent. De eenvoudigste manier om
een PropertyChangeEvent te genereren is door gebruik te
maken van de methode firePropertyChange van
de hulpklasse java.beans.PropertyChangeSupport.
Daarnaast moet een bean met een of meer gebonden eigenschappen
ook de methoden addPropertyChangeListener en
removePropertyChangeListener implementeren:
public void addPropertyChangeListener(PropertyChangeListener l); public void removePropertyChangeListener(PropertyChangeListener l);
Ook dit kan eenvoudig worden opgelost door de gelijknamige methoden
van de hulpklasse PropertyChangeSupport op te roepen.
We maken een bean die bestaat uit een getal en een bijhorende tekst
(zowel visueel als in de vorm van design-time eigenschappen).
Veranderingen in het getal genereren een PropertyChangeEvent.
Het getal is een int, maar de methode firePropertyChange
ontvangt een Integer omdat ze geen primitieve gegevenstypes
aanvaardt.
import java.awt.*;
import javax.swing.*;
import java.beans.*;
public class TekstEnGetal extends JPanel {
private int hetGetal = 0;
private String deTekst = "";
private JLabel txtGetal = new JLabel(hetGetal + "");
private JLabel txtTekst = new JLabel(deTekst);
private PropertyChangeSupport sup
= new PropertyChangeSupport(this);
public TekstEnGetal() {
setLayout(new GridLayout(2, 1, 10, 10));
add(txtTekst);
add(txtGetal);
}
public int getWaarde() {
return hetGetal;
}
public void setWaarde(int w) {
Integer oudeWaarde = new Integer(hetGetal);
hetGetal = w;
sup.firePropertyChange("waarde", oudeWaarde, new Integer(hetGetal));
txtGetal.setText(hetGetal + "");
}
public String getTitel() {
return deTekst;
}
public void setTitel(String t) {
deTekst = t;
txtTekst.setText(deTekst);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
sup.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
sup.removePropertyChangeListener(l);
}
}
Om een dergelijke gebeurtenis op te vangen, moet een bean (meestal een
andere bean) de interface java.beans.PropertyChangeListener
implementeren. Het mechanisme is hetzelfde als bij het opvangen van
gebeurtenissen in een grafische gebruikersinterface (zie paragraaf 5.3):
één component genereert een gebeurtenis, een ander object
handelt de gebeurtenis af. De koppeling van de twee wordt tot stand gebracht
door een oproep van de methode addPropertyChangeListener
van de eerste.
Het verschil ligt hier niet zozeer in de programmacode, maar in de
wijze waarop de programmacode wordt gemaakt. De koppeling kan namelijk
automatisch tot stand worden gebracht door de ontwikkelingsomgeving
(bijvoorbeeld de BeanBox) door een zogenaamde "adaptor class" te
genereren. We zullen de aanroep van de methode addPropertyChangeListener
dus niet intypen, maar genereren vanuit de BeanBox.
De interface PropertyChangeListener bestaat slechts uit
één methode:
public void propertyChange(PropertyChangeEvent e);
We maken de bean RoodVlak van paragraaf 5.2 gevoelig
voor de veranderingen in een bean van het type TekstEnGetal.
Als de getalwaarde verandert, gaat de afhandelaar aan de bean die de
gebeurtenis veroorzaakte, zijn titel opvragen. Als dit één van
de drie woorden "rood", "groen" of "blauw" is, dat wordt de kleur van het
paneel aangepast: de overeenkomstige kleurcomponent krijgt de gegeven
getalwaarde tussen 0 en 255.
import java.awt.*;
import java.beans.*;
public class KleurVlak
extends Canvas implements PropertyChangeListener {
public KleurVlak() {
setSize(60, 40);
setBackground(Color.red);
}
public void propertyChange(PropertyChangeEvent e) {
Object bron = e.getSource();
if (bron instanceof TekstEnGetal) {
TekstEnGetal teg = (TekstEnGetal) bron;
String titel = teg.getTitel();
Color c = getBackground();
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
Integer waarde = (Integer) e.getNewValue();
if (titel.equalsIgnoreCase("rood"))
setBackground(new Color(waarde.intValue(), g, b));
else if (titel.equalsIgnoreCase("groen"))
setBackground(new Color(r, waarde.intValue(), b));
else if (titel.equalsIgnoreCase("blauw"))
setBackground(new Color(r, g, waarde.intValue()));
}
}
}
Verschillende beans in eenzelfde container beschikken niet
noodzakelijk over referenties naar elkaar. In het bijzonder
weet de bean die voor de afhandeling van een gebeurtenis
verantwoordlijk is, niet altijd bij welke bronbean ze zich
als Listener moet registreren. Het is aan de
ontwikkelingsomgeving (bijvoorbeeld de BeanBox) om de
ontwerper de verbinding op grafische wijze te laten leggen.
We illustreren dit door een verband te leggen tussen een exemplaar
van de twee klassen TekstEnGetal en KleurVlak
hierboven.
Importeer beide beans in de BeanBox. Je kan dit bijvoorbeeld doen
via eenzelfde archief, met als manifest (in het bestand
TekstEnGetal.txt)
Name: TekstEnGetal.class Java-Bean: True Name: KleurVlak.class Java-Bean: True
en met de DOS-commandos
javac TekstEnGetal.java KleurVlak.java jar cfm TekstEnGetal.jar TekstEnGetal.txt TekstEnGetal.class KleurVlak.class
Start de BeanBox en gebruik het menu-item File/LoadJar... om het archief
TekstEnGetal.jar te laden. In de Toolbox moeten nu onderaan
twee nieuwe bean-types beschikbaar zijn: TekstEnGetal en
KleurVlak. Breng van elk van beide types telkens
één exemplaar in het BeanBox-ontwerpvenster aan.
Activeer de tweede component (die van het type TekstEnGetal) en sleep aan een van
zijn hoeken zodat hij een tweetal centimeter breed wordt. Kies het menu-item
Edit/Events/propertyChange/propertyChange. De muisaanwijzer begeleidt nu het
uiteinde van een lijn waarvan het beginpunt vastgemaakt is aan de actieve bean.
Klik op de andere bean (van het type KleurVlak). Het lijnstuk verdwijnt
en er verschijnt een dialoogvenster met alle publieke methoden van de klasse
KleurVlak. Selecteer de methode propertyChange en druk op OK.
De BeanBox genereert nu intern een nieuwe klasse die voor de communicatie tussen de twee beans zorgt. Een dergelijke klasse staat in handboeken over object-georiënteerd ontwerp bekend als een adaptor, zoals ook het dialoogvenster vermeldt.
We zijn nu klaar om gebeurtenissen te generern. Zet via het
venster Properties de eigenschappen titel en waarde
van de bean TekstEnGetal
respectievelijk op blauw en 255. Dit laatste produceert
een gebeurtenis van het type PropertyChangeEvent. De bean
KleurVlak detecteert dat deze gebeurtenis afkomstig is van
een TekstEnGetal met titel "blauw", en past dus zijn kleur aan
door de blauwe component van de rgb-waarden op 255 te zetten (de rode
component was al 255, het resultaat is dus magenta).
Door dezelfde bean daarna achtereenvolgens de titels "groen" en "rood"
te geven, kan je de rechthoek eender welke kleur doen aannemen. Je kan natuurlijk
ook gebeurtenissen laten afvuren door drie verschillende exemplaren van het
beantype TekstEnGetal.
De adaptor class die intern gegenereerd is, heeft niets magisch. Ze bevindt zich
bijvoorbeeld in de map c:\bdk1.1\beanbox\tmp\sunw\beanbox en ziet
er ongeveer als volgt uit.
// Automatically generated event hookup file.
package tmp.sunw.beanbox;
import KleurVlak;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
public class ___Hookup_1700aa3ba8
implements java.beans.PropertyChangeListener, java.io.Serializable {
public void setTarget(KleurVlak t) {
target = t;
}
public void propertyChange(java.beans.PropertyChangeEvent arg0) {
target.propertyChange(arg0);
}
private KleurVlak target;
}
Het adaptor-object wordt geconstrueerd met behulp van het KleurVlak-object
dat de gebeurtenissen moet ontvangen. Het adaptor-object wordt geregistreerd
(door de omgeving) als
een PropertyChangeListener die geïnteresseerd is in dergelijke
events vanwege het TekstEnGetal-object.
In voorbereiding
In voorbereiding
Een PropertyChangeEvent (zie paragraaf 8.3.2) is
niet de enige soort gebeurtenis die door JavaBeans kan worden
voortgebracht. De ontwerper kan ook zelf nieuwe soorten gebeurtenissen
definiëren. Nieuwe types van gebeurtenissen
moeten aan de volgende naamconventies voldoen.
We gaan ervan uit dat de gebeurtenis de naam G draagt. In onze voorbeelden gaan we ervan uit dat we een nieuwe soort gebeurtenis, KleurGewijzigd, willen definiëren.
GEvent
die rechtstreeks of onrechtstreeks is afgeleid van de klasse
java.util.EventObject. Bijvoorbeeld
public class KleurGewijzigdEvent extends java.util.EventObject {
// ...
}
Hou er rekening mee dat de constructor van de klasse EventObject
een parameter verwacht (het object dat de gebeurtenis voortbrengt), dus
je eigen klasse zal waarschijnlijk een constructor hebben die begint met
een aanroep van super(...).
GListener
die rechtstreeks of onrechtstreeks is afgeleid van de interface
java.util.EventListener. De interface heeft nul of meer methoden,
maar ze moeten allemaal één argument van het type GEvent
verwachten en terugkeertype void hebben.
Bijvoorbeeld
public interface KleurGewijzigdListener extends java.util.EventListener {
void kleurGewijzigd(KleurGewijzigdEvent e);
}
GListeners
hun interesse kunnen kenbaar maken en ongedaan maken:
public void addGListener(GListener l) {
// ...
}
public void removeGListener(GListener l) {
// ...
}
In de meeste implementaties zullen deze methoden gewoon een
dynamische lijst bijhouden met alle actieve luisteraars,
bijvoorbeeld in de vorm van een private Vector:
private Vector kleurListeners = new Vector();
public void addKleurGewijzigdListener(KleurGewijzigdListener l) {
kleurListeners.add(l);
}
public void removeKleurGewijzigdListener(KleurGewijzigdListener l) {
kleurListeners.remove(l);
}
In het voorbeeld van paragraaf 8.3.2 moest de ontvanger van een
PropertyChangeEvent nog de methode getTitel
van de bron van de gebeurtenis oproepen omdat de titel geen deel
uitmaakte van de gebeurtenis-informatie. Een PropertyChangeEvent
bevat namelijk alleen informatie over de oude en nieuwe waarden van
de gewijzigde eigenschap zelf, in dit geval de eigenschap waarde.
We herschrijven dit voorbeeld nu aan de hand van het nieuwe gebeurtenistype
KleurGewijzigdEvent dat hierboven reeds werd aangekondigd.
De nieuwe versies van de oorsprong en afhandelaar van de gebeurtenis
heten nu TekstEnGetal2 resp. KleurVlak2.
We beginnnen met de definitie van het gebeurtenistype zelf.
/** Gebeurtenis die aangeeft dat een waarde getiteld "rood",
* "groen" of "blauw" is gewijzigd. Deze gebeurtenissen
* zijn ondermeer afkomstig van JavaBeans van het type
* TekstEnGetal2.
* @see TekstEnGetal2
*/
public class KleurGewijzigdEvent extends java.util.EventObject {
/** Symbolische constante die aankondigt dat
* de rode kleurcomponent wijzigt.
*/
public static final int ROOD = 100;
/** Symbolische constante die aankondigt dat
* de groene kleurcomponent wijzigt.
*/
public static final int GROEN = 200;
/** Symbolische constante die aankondigt dat
* de blauwe kleurcomponent wijzigt.
*/
public static final int BLAUW = 300;
/** Symbolische constante die aankondigt dat
* de gewijzigde kleurcomponent niet bepaald
* kon worden.
*/
public static final int ONBEPAALD = 400;
/** De component waarvan de bijdrage wijzigt. */
private int component = ONBEPAALD;
/** De bijdrage van de kleurcomponent. */
private int waarde = 0;
/** Construeer een gebeurtenis die aangeeft dat
* een kleurcomponent wijzigt.
* @param bron
* De oorsprong van de gebeurtenis.
* @param component
* Symbolische constante die aangeeft welke
* van de drie hoofdkleuren (rood, groen, blauw)
* gewijzigd is. Moet de waarde KleurGewijzigdEvent.ROOD,
* KleurGewijzigdEvent.GROEN of KleurGewijzigdEvent.BLAUW
* aannemen.
* @param waarde
* Geheel getal tussen 0 en 255 dat de nieuwe specifieke
* intensiteit in de gegeven hoofdkleur aangeeft.
*/
public KleurGewijzigdEvent(Object bron, int component, int waarde) {
super(bron);
if (component == ROOD || component == GROEN || component == BLAUW)
this.component = component;
else
this.component = ONBEPAALD;
if (waarde < 0)
this.waarde = 0;
else if (waarde > 255)
this.waarde = 255;
else
this.waarde = waarde;
}
/** Geef aan welke van de drie hoofdkleuren wijzigt.
* @returns
* Een van de symbolische constanten KleurGewijzigdEvent.ROOD,
* KleurGewijzigdEvent.GROEN of KleurGewijzigdEvent.BLAUW.
*/
public int getComponent() {
return component;
}
/** Geef aan wat de nieuwe specifieke intensiteit in de gegeven
* hoofdkleur is.
* @returns
* De specifieke intensiteit, een geheel getal tussen 0
* (afwezig) en 255 (maximale intensiteit).
*/
public int getWaarde() {
return waarde;
}
}
De belangrijkste wijziging in de klasse TekstEnGetal2 ten opzichte
van haar voorganger TekstEnGetal ligt in het feit dat we nu zelf
de Vector van luisteraars bijhouden in plaats van dit over te laten
aan een PropertyChangeSupport. Dit uit zich vooral in de
nieuwe private methode lanceerWijziging,
die bij aanpassingen van de eigenschap "waarde"
de nodige verwittigingen rondstuurt door over de componenten van de
Vector te itereren.
import java.awt.*;
import javax.swing.*;
import java.util.Vector;
import java.util.Enumeration;
public class TekstEnGetal2 extends JPanel {
private int hetGetal = 0;
private String deTekst = "";
private JLabel txtGetal = new JLabel(hetGetal + "");
private JLabel txtTekst = new JLabel(deTekst);
private Vector kleurListeners = new Vector();
public TekstEnGetal2() {
setLayout(new GridLayout(2, 1, 10, 10));
add(txtTekst);
add(txtGetal);
}
public int getWaarde() {
return hetGetal;
}
public void setWaarde(int w) {
Integer oudeWaarde = new Integer(hetGetal);
hetGetal = w;
if (getTitel().equalsIgnoreCase("rood"))
lanceerWijziging(KleurGewijzigdEvent.ROOD);
else if (getTitel().equalsIgnoreCase("groen"))
lanceerWijziging(KleurGewijzigdEvent.GROEN);
else if (getTitel().equalsIgnoreCase("blauw"))
lanceerWijziging(KleurGewijzigdEvent.BLAUW);
txtGetal.setText(hetGetal + "");
}
private void lanceerWijziging(int component) {
Enumeration e = kleurListeners.elements();
while (e.hasMoreElements()) {
KleurGewijzigdListener k = (KleurGewijzigdListener) e.nextElement();
k.kleurGewijzigd(new KleurGewijzigdEvent(this, component, getWaarde()));
}
}
public String getTitel() {
return deTekst;
}
public void setTitel(String t) {
deTekst = t;
txtTekst.setText(deTekst);
}
public void addKleurGewijzigdListener(KleurGewijzigdListener l) {
kleurListeners.add(l);
}
public void removeKleurGewijzigdListener(KleurGewijzigdListener l) {
kleurListeners.remove(l);
}
}
De klasse KleurVlak2 wordt vooral een stuk eenvoudiger
dan haar voorganger KleurVlak, omdat de detectie van het
soort kleurcomponent nu reeds vóór het verzenden van
de gebeurtenis plaatsgrijpt. De methode propertyChange
wordt vervangen door een veel eenvoudiger kleurGewijzigd.
import java.awt.*;
public class KleurVlak2
extends Canvas implements KleurGewijzigdListener {
public KleurVlak2() {
setSize(60, 40);
setBackground(Color.red);
}
public void kleurGewijzigd(KleurGewijzigdEvent e) {
Color c = getBackground();
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
switch (e.getComponent()) {
case KleurGewijzigdEvent.ROOD:
setBackground(new Color(e.getWaarde(), g, b));
break;
case KleurGewijzigdEvent.GROEN:
setBackground(new Color(r, e.getWaarde(), b));
break;
case KleurGewijzigdEvent.BLAUW:
setBackground(new Color(r, g, e.getWaarde()));
break;
}
}
}
De samenwerking van beide Beans in de BeanBox verloopt uiterlijk op dezelfde
manier als tevoren. Hier is nog een tafereeltje waarbij de kleur van de
rechthoek wordt gedicteerd door drie afzonderlijke beans van het type
TekstEnGetal2:
In voorbereiding
In voorbereiding
In voorbereiding
In voorbereiding