![]() |
|
Swing-Komponenten zeichnen sich dadurch aus, dass der JavaBeans-Standard unterstützt wird (zu JavaBeans siehe Kapitel 9). Somit eignen sich Swing-Komponenten auch hervorragend für den Einsatz in Entwicklungsumgebungen (Rapid Application Development, RAD). Swing-Komponenten basieren auf der Model-View-Controller-Architektur. Die Grundidee dieser Architektur besteht darin, die Verarbeitung der eigentlichen Daten von ihrer Darstellung im User Interface zu trennen (siehe Kapitel 2). Die Daten werden hierbei zentral und unabhängig von ihrer grafischen Repräsentation gespeichert, wobei eine derartige Datenbasis als Datenmodell bezeichnet wird. Eine Komponente der Benutzeroberfläche, die View, die diese Daten darstellt, bezieht die Daten dann aus dem Modell und speichert sie nicht mehr selbst. Schließlich sorgt der Controller dafür, dass das Modell den Benutzereingaben entsprechend geändert wird. Dieser Ansatz weist gegenüber der Speicherung aller relevanten Daten im GUI-Element selbst einige Vorteile auf. Es wird einfacher, Änderungen an der Benutzeroberfläche durchzuführen, ohne tiefer in die Struktur des eigentlichen Programm-Codes einzugreifen zu müssen. Außerdem ist die mehrfache Darstellung einer Datenbasis effizienter, da die eigentlichen Daten nur einmal zentral gespeichert werden. Ferner ermöglicht es diese Art der Trennung, eine klarere Struktur der Anwendung zu erreichen, was sowohl der Erweiterbarkeit als auch der Wiederverwendbarkeit des Codes zuträglich ist. Die Nachteile des Ansatzes bestehen darin, dass einerseits die Anpassung bestehender Programme an Swing einen höheren Aufwand mit sich bringen kann und dass andererseits die Entwicklung von Anwendungen, bei denen sich der Mehraufwand für eine vollständige Umsetzung des MVC-Konzeptes nicht lohnt, erschwert wird. Die ersten Versuche von JavaSoft, die MVC-Architektur an das Swing-Komponentenmodell anzupassen, scheiterten, was zu einer Modifikation dieser Architektur führte. Die modifizierte MVC-Architektur wird auch als Model-Delegator-Architektur bezeichnet. Somit ist in Swing die Trennung von Modell und View (Sicht) bei den meisten Komponenten klar, wohingegen der Controller nicht klar abgegrenzt ist. So ist es bei einigen Komponenten möglich, Eingaben des Benutzers in Modelländerungen ohne einen expliziten Controller umzusetzen. Der Controller ist also praktisch in das GUI-Element integriert. Bei anderen Elementen übernimmt zum Teil ein Listener-Objekt diese Rolle.
Abb. 8.4: Die modifizierte MVC-Architektur (Model-Delegator) UI-Delegator und UI-Manager Der sog. Delegator ist das UI-Objekt, das sowohl für die View als auch für den Controller zuständig ist. Die Klasse UIManager verfügt über die Methode setLookAndFeel(), die dazu verwendet wird, das gewünschte Aussehen (Look and Feel) und das Verhalten der verwendeten Komponente festzulegen. Hierzu werden folgende Methoden verwendet:
UIManager.setLookAndFeel(UIManager.getCrossPlattform LookAndFeelClassName()); } catch (Exception exp) { System.out.println("Das gewünschte Look and Feel kann nicht gesetzt werden wegen: "+ exp); } Folgende Exceptions können erzeugt werden:
Modell Swing unterscheidet zwischen zwei Grundtypen von Modellen. Zum einen gibt es Datenmodelle, die benutzt werden, um für die Anwendung relevante Daten zu speichern. Zum anderen existieren auch GUI-Statusmodelle, die den Zustand einer GUI-Komponente darstellen, und die somit Informationen beinhalten, die spezifisch für Benutzeroberflächen sind. Ein Beispiel dafür ist das Im Folgenden wird auf die Datenmodelle in einer allgemeinen Art und Weise eingegangen, da die hinter beiden Modelltypen stehenden Konzepte sehr ähnlich sind. Allgemein ist das Model-View-Controller-Konzept in Swing folgendermaßen umgesetzt:
Verfügt man über ein Datenmodell, so kann man daran beliebig viele passende GUI-Komponenten als Views anschließen. Dazu übergibt man eine Instanz der Datenmodellklasse als Argument an den Konstruktor der Komponente. Implizit wird dabei die GUI-Komponente beim Modell als Listener angemeldet. Je nach Komponente wird auch umgekehrt das Modell bei der View als Listener angemeldet. In diesem Fall ist kein expliziter Controller mehr nötig. Wenn für eine verwendete Swing-Komponente ein Modell nicht explizit definiert wurde, wird das Standardmodell verwendet. So erzeugt Swing bei der Instantiierung eines JSlider-Objekts bspw. ein Standardmodell-Interface, das die Anwendung benutzen kann, um den aktuellen Wert, das Maximum oder das Minimum eines Sliders zu setzen.
this.model = new DefaultBoundedRangeModel(value, 0, min, max); Bei der Erzeugung eines Modells verwendet Swing die BoundProperty-Eigenschaft von JavaBeans. Wird ein anderes Modell gewünscht, so muss die Methode setModel() überschrieben werden. Folgendes Beispiel verdeutlicht dies:
JSlider mySlider = new JSlider(); public void setValue(int currentValue){ System.out.println("Der Wert ist :", currentValue); } } Die ersten zwei Zeilen erzeugen jeweils eine Instanz der Klasse JSlider bzw. JProgressBar, anschließend wird ein Modell instantiiert, das die Standardimplementierung des BoundedRangeModel-Interfaces darstellt. An dieses Modell werden dann zwei Views angeschlossen, JSlider sowie JProgressBar. Bewegt man den Schieberegler, so passt sich der dazugehörige Balken automatisch an den neuen Wert an, wobei keine Ereignisse explizit verarbeitet werden.
Abb. 8.5: Das Swing1-Beispiel: Ein Datenmodell mit zwei Views Hierbei tritt der Fall ein, dass eine View – in diesem Fall die JSlider-Komponente – implizit auch die Rolle des Controllers übernimmt, also Eingaben des Benutzers, die durch Bewegungen des Schiebereglers eingegeben wurden, in eine passende Änderung der Daten im Modell umsetzt.
//* Beispiel Swing1.java fuer das Buch OpenJava import java.awt.*; BoundedRangeModel myModel = new DefaultBoundedRangeModel(30,1,0,100); // Konstruktor super("Swing 1"); Hierbei wird ein einfaches Datenmodell angenommen, natürliche Zahlen zwischen 0 und 100, wobei der Anfangswert des Sliders auf 30 gesetzt wird. Zur Anzeige des Datenmodells werden zwei Swing-Komponenten gewählt: Ein JSlider-Objekt und ein JProgressBar-Objekt. JSlider ändert den Wert des Datenmodells, wobei JProgressBar den neuen Wert anzeigt.
JSlider mySlider = new JSlider(myModel); Die letzten vier Zeilen betreffen die Darstellung der verschiedenen JSlider-Eigenschaften, darunter die Kontrollzeichen, die sog. Labels. Weiterhin werden der maximale und der minimale Abstand der Kontrollzeichen angegeben. Im Folgenden wird das Layout des Panels auf BoxLayout gesetzt. In einem Box-Layout werden die enthaltenen Komponenten in vertikaler Richtung angeordnet.
myJPanel.setLayout(new BoxLayout(myJPanel, BoxLayout.Y_AXIS)); } JFrame frame = new Swing1(); public void windowClosing(WindowEvent e) { System.exit(0); } }; frame.addWindowListener(l); } } Möchte man dieses einfache Beispiel um eine Komponente erweitern, in diesem Fall um ein JTextField-Objekt, das den aktuellen Wert der Zahl in myModel oder das Ergebnis einer Berechnung mit dieser Zahl als Argument anzeigt, so hat man das Problem, dass ein Objekt vom Typ JTextField nicht ohne weiteres als View für ein Bounded-RangeModel benutzt werden kann. Ein Objekt vom Typ JTextField verwendet normalerweise eine Instanz einer Klasse als Datenmodell, die das Interface Document implementiert. Eine einfache und zugleich elegante Lösung besteht hier in einer Adapterklasse. Eine typische Adapterklasse könnte im Wesentlichen folgendermaßen aussehen:
class IntegerToTextAdapter implements ChangeListener { BoundedRangeModel myModel; myModel= theModel; } public void stateChanged(ChangeEvent e) { String derWert= (new Integer(myModel.getValue())).toString(); } } Im Konstruktor der Adapterklasse wird das Objekt dann beim Modell als Listener angemeldet. Tritt ein ChangeEvent-Event auf, so liest der Adapter den aktuellen Wert aus dem Modell, führt daraufhin die gewünschten Berechnungen aus und schreibt das Ergebnis schließlich in das entsprechende JTextField-Objekt (myTextField). Durch die Programmzeile
IntegerToTextAdapter myAdapter = new IntegerToTextAdapter(myModel,myTextField); wird eine Verbindung zwischen dem Modell und der neu hinzugekommenen JTextField-Komponente durch ein Adapterobjekt hergestellt. Eine erweiterte Version des ersten Beispiels könnte dann folgendermaßen aussehen:
//* Beispiel Swing2.java für das Buch OpenJava import java.awt.*; // Adapterklasse: Ermoeglicht die Nutzung eines JTextField als class IntegerToTextAdapter implements ChangeListener { BoundedRangeModel myModel; myModel=theModel; // Adapter als Listener beim Model anmelden } public void stateChanged(ChangeEvent e) { // Model hat sich geaendert: Text soll angepasst werden } } public class Swing2 extends JFrame { // Einfaches Datenmodell: Natuerliche Zahlen zwischen 0 und BoundedRangeModel myModel = new DefaultBoundedRangeModel(30,1,0,100); // Konstruktor super("Swing 2"); JTextField myTextField = new JTextField(); // Layout myJPanel.setBorder(BorderFactory.createEmptyBord er(10,10 ,10,10)); // Ein JTextField kann normalerweise nicht zusammen mit IntegerToTextAdapter myAdapter = new IntegerToTextAdapter(myModel,myTextField); myTextField.setEditable(false); } // Main Funktion ohne Argumente public static void main(String[] args) { JFrame frame = new Swing2(); WindowListener l = new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }; frame.addWindowListener(l); } } |
|
|