Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 44 - Beans

## 44.5 Panel-Beans

- 44.5 Panel-Beans

Die bisherigen Beans waren aus `Canvas` abgeleitet und repräsentierten damit elementare Komponenten. Ebenso leicht ist es möglich, zusammengesetzte Komponenten als Beans zu definieren und so größere Programmeinheiten wiederverwendbar zu machen.

Wird eine Bean beispielsweise aus der Klasse `Panel` abgeleitet, können darauf vordefinierte Dialogelemente plaziert und mit Hilfe von setter-Methoden konfigurierbar gemacht werden. Im GUI-Designer erscheinen die unterschiedlichen Dialogelemente dann wie ein einziges. Meist spielt die Bean auch noch die Rolle eines Ereigniskonzentrators, d.h. sie registriert sich als Listener bei ihren eigenen Komponenten und verarbeitet diese so weit wie möglich selbst. Nach außen hin definiert sie ein abstrakteres Event-Modell, das an die Bedürfnisse der Gesamtkomponente angepaßt ist. Statt den Client mit den Elementarereignissen der einzelnen Komponenten zu belasten, sendet sie nur noch Ereignisse, die das Verhalten der zusammengesetzten Komponente repräsentieren.

Die Konstruktion einer Panel-Bean ist prinzipiell nicht schwieriger als die einer einfachen Bean. Zusätzlicher Aufwand wird allerdings erforderlich, wenn die Eigenschaften der darin enthaltenen Komponenten zum Designzeitpunkt nicht mit einfachen getter- und setter-Methoden erledigt werden kann. Die meisten Entwickler erwarten heute beispielsweise von einem Splitpanel (eine typische Panel-Bean), daß im GUI-Designer die einzelnen Komponenten per Drag-and-Drop darauf plaziert werden können.

So weit wollen wir allerdings nicht in die Bean-Programmierung vordringen. Wir wollen nur die Grundprinzipien erklären und eine Klasse `ButtonPanel` entwickeln, die eine Gruppe von Radiobuttons kapselt. Die Anzahl der Radiobuttons, ihre Beschriftung und die Auswahl des selektierten Elements sollen frei definiert werden können. Ändert sich die Anzahl der Buttons oder wird ein anderer Button selektiert, soll die Bean ein `PropertyChangeEvent` versenden.

Das folgende Listing zeigt den Code für diese Klasse:

    001 /* ButtonPanel.java */
    002
    003 import java.awt.*;
    004 import java.awt.event.*;
    005 import java.beans.*;
    006 import java.io.*;
    007
    008 public class ButtonPanel
    009 extends Panel
    010 implements Serializable, ItemListener
    011 {
    012   //---Instanzvariablen------------------------------------
    013   protected CheckboxGroup cbg;
    014   protected Checkbox[]    cb;
    015   transient protected PropertyChangeSupport pcs;
    016
    017   //---Methoden--------------------------------------------
    018   public ButtonPanel()
    019   {
    020     cbg = new CheckboxGroup();
    021     cb = new Checkbox[0];
    022     initTransientState();
    023   }
    024
    025   //---Anzahl der RadioButtons-----------------------------
    026   public void setButtonCnt(int cnt)
    027   {
    028     if (cnt != cb.length) {
    029       int oldvalue = cb.length;
    030       //Bisherige Buttons entfernen
    031       if (cb.length > 0) {
    032         removeAll();
    033       }
    034       cb = new Checkbox[cnt];
    035       setLayout(new GridLayout(cnt, 1));
    036       for (int i = 0; i < cnt; ++i) {
    037         cb[i] = new Checkbox(
    038           "RadioButton " + i,
    039           cbg,
    040           (i == 0 ? true : false)
    041         );
    042         cb[i].addItemListener(this);
    043         add(cb[i]);
    044       }
    045       //PropertyChangeEvents senden
    046       pcs.firePropertyChange("buttonCnt", oldvalue, cnt);
    047       if (cnt > 0) {
    048         setSelected(0);
    049       }
    050       //Neu layouten
    051       setSize(getPreferredSize());
    052       invalidate();
    053       doLayout();
    054       Container owner = getParent();
    055       if (owner != null) {
    056         owner.invalidate();
    057         owner.doLayout();
    058       }
    059     }
    060   }
    061
    062   public int getButtonCnt()
    063   {
    064     return cb.length;
    065   }
    066
    067   //---Beschriftung der Buttons----------------------------
    068   public void setLabel(int index, String label)
    069   {
    070     if (index >= 0 && index < cb.length) {
    071       cb[index].setLabel(label);
    072     }
    073   }
    074
    075   public String getLabel(int index)
    076   {
    077     String ret = "***invalid index***";
    078     if (index >= 0 && index < cb.length) {
    079       ret = cb[index].getLabel();
    080     }
    081     return ret;
    082   }
    083
    084   //---Selektiertes Element--------------------------------
    085   public void setSelected(int index)
    086   {
    087     if (index >= 0 && index < cb.length) {
    088       int oldvalue = getSelected();
    089       cb[index].setState(true);
    090       pcs.firePropertyChange("selected", oldvalue, index);
    091     }
    092   }
    093
    094   public int getSelected()
    095   {
    096     int ret = -1;
    097     for (int i = 0; i < cb.length; ++i) {
    098       if (cb[i].getState()) {
    099         ret = i;
    100         break;
    101       }
    102     }
    103     return ret;
    104   }
    105
    106   //---Verwaltung der PropertyChangeListener---
    107   public void addPropertyChangeListener(
    108     PropertyChangeListener l
    109   )
    110   {
    111     pcs.addPropertyChangeListener(l);
    112   }
    113
    114   public void removePropertyChangeListener(
    115     PropertyChangeListener l
    116   )
    117   {
    118     pcs.removePropertyChangeListener(l);
    119   }
    120
    121   //---Reaktion auf Zustandsänderungen---------------------
    122   public void itemStateChanged(ItemEvent event)
    123   {
    124     Checkbox changedcb = (Checkbox) event.getItemSelectable();
    125     if (changedcb.getState()) {
    126       for (int i = 0; i < cb.length; ++i) {
    127         if (cb[i] == changedcb) {
    128           pcs.firePropertyChange("selected", -1, i);
    129           break;
    130         }
    131       }
    132     }
    133   }
    134
    135   //---Private Methoden------------------------------------
    136   /**
    137    * Initialisierung der nicht-persistenten Instanzvariablen.
    138    */
    139   private void initTransientState()
    140   {
    141     pcs = new PropertyChangeSupport(this);
    142   }
    143
    144   /**
    145    * Wird überlagert, um nach dem Deserialisieren den transienten
    146    * Zustand zu initialisieren.
    147    */
    148   private void readObject(ObjectInputStream stream)
    149   throws IOException, ClassNotFoundException
    150   {
    151     stream.defaultReadObject();
    152     initTransientState();
    153   }
    154 }
| ButtonPanel.java

Listing 44.9: Die Klasse ButtonPanel

Die Bean verwaltet die Radionbuttons in dem Array `cbg`, das zunächst leer ist. Verändert sich ihre Anzahl durch Aufruf von `setButtonCnt`, werden alle bisherigen Buttons entfernt, neue in der gewünschten Anzahl angelegt und die registrierten Listener über die Änderung benachrichtigt. Zunächst haben die Buttons die Beschriftung "RadioButton 1", "RadioButton 2" usw. Sie kann durch Aufruf von `setLabel` geändert werden. Der Zugriff auf das selektierte Element erfolgt mit `getSelected` und `setSelected`.

Um benachrichtigt zu werden, wenn der Benutzer per Mausklick die Selektion ändert, registriert sich die Bean in Zeile 042 als `ItemListener` bei den von ihr erzeugten Radiobuttons. Dadurch wird bei jeder Änderung die Methode `itemStateChanged` aufgerufen, und die Bean kann ihrerseits ein `PropertyChangeEvent` an alle registrierten Listener versenden.

Abbildung 44.7 zeigt ein `ButtonPanel` mit vier Radiobuttons in der Beanbox:

Abbildung 44.7: Das ButtonPanel in der BeanBox

|  |  |  | Das Listing treibt einigen Aufwand, um nach einer Größenänderung korrekt dargestellt zu werden (im Listing ab Zeile 050 zu sehen). Trotzdem war beispielsweise die Beanbox nicht dazu zu bewegen, bei einer Verminderung der Buttonzahl den nicht mehr benötigten Platz wieder freizugeben. Ist das dynamische Ändern der Größe ein wichtiges Feature, muß unter Umständen noch ein wenig experimentiert werden, um bei allen Clients zu den gewünschten Resultaten zu kommen. |  |
Warnung

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de