Umsetzung der Fenster-Klasse als Java-Bean
Vor- und Nachteile der Ereignisbehandlung über NetBeans-IDE oder alternativ über selbst implementierte Mechanismen
Im vorangegangenen Kapitel wurde Funktionalität für GUI-Elemente durch automatisierte Generierung von Lstener-Methoden innerhalb von NetBeans-IDE realisiert.
Davor wurde bereits mit einem slebst geschriebenen Listener in der Fenster-Klasse gearbeitet.
Die direkte Nutzung von NetBeans-IDE für die Ereignisbehandlung hat folgende Vorteile:
- Die Verknüpfung zwischen den Elementen arbeitet schneller, als die Verarbeitung in einer Methode zu konzentrieren.
- Das Einfügen der Listener erfolgt intuitiv, manchmal ohne vorherige Kenntnis der Listener-Methoden.
|
Die direkte Nutzung von NetBeans-IDE für die Ereignisbehandlung hat folgende Nachteile:
- Durch die ausschließliche Präsentation des selber zu ergänzenden Codeausschnitts und das automatische Generieren aller Listener in der Fenster-Klasse verliert man schnell den Überblick über den Quellcode.
- Schreibt man selber die Listener, kann man Funktionalitäten zusammenfassen. Schon bei dem einfachen Beispiel zuvor mußte redundanter Code an zwei Stellen eingefügt werden, was auch dessen Wartung schwieriger macht.
|
Idee für ein Gesamtkonzept für das Einbeziehen der NetBeans-IDE in die Entwicklung mechatronischer Anwendungen
- JInternalFrame verhält sich wie JFrame, ist aber ein Element, das sich in ein anderes Fenster platzieren läßt.
- Mehrere solcher interner Fenster könnten direkt in dem Hauptfenster platziert werden.
- In der nachfolgend entwickelten Anwendung werden aber Karteireiter (JTabbedPane) mit eingebetteter Scrollfläche (JScrollPane) verwendet.
- Dies erlaubt es relativ viele interne Fenster innerhalb einer Anwendung zu platzieren, die zudem auch größer als das zu sehende Fenster sein dürfen.
- Anwendungsidee aus der Simulations/Regelungstechnik: Fünf Karteireiter repräsentieren die Objekte Optimierer, Gütefunktion, Simulator, Integrator, Modell.
- Anwendungsidee aus Mikroprozessor-Anwendungen: Vier Karteireiter repräsentieren die Objekte Zeitgeber, Sensor, Regler, Antrieb.
- Idee: Objekte eines Karteikastens versuchen sich automatisch als Objekte bei den anderen Karteikastenmitgliedern zu registrieren: Modell bei Integrator bei Simulator bei Gütefunktion bei Optimierer.
- Visualisierungen, Datei-Ausgaben, Animationen etc. sollten aber nicht als Extra-Karteikarte umgesetzt werden, sondern einer bestehenden als Java-Bean hinzugefügt werden, ähnlich wie die Double-Wert-Eingabe.
|
Bild 0-1: Screenshot einer mit NetBeans-IDE realisierten Testanwendung mit Karteikarten (JTabbedPanes).
Modifikation der Klasse Fenster als erbend von JInternalFrame, neuer Name: InternesFenster
- In der nachfolgend besprochenen Anwendung wird aber zunächst nur unser Standard-Summierungsbeispiel umgesetzt.
- Für den "Karteikasten" werden zunächst Standard-Java-Beans (Palette - Swing Containers - Tabbed Pane, Scroll Pane) verwendet.
- In der zu erstellenden .jar-Datei sollen beide Java-Beans verfügbar sein: InternesFenster und DoubleParameterBean, so wird deutlich, wie größere Bibliotheken erstellt werden können.
- Als Vorbereitung wird zunächst wieder eine Anwendung ohne NetBeans-IDE erstellt:
- Die DoubleParameterBean-Objekte werden in ein InternesFenster-Objekt gelegt und dieses in ein Fenster-Objekt.
|
Bild 0-2: Screenshot der zum Test gestarteten Anwendung mit internem Fenster.
- Die neue Vererbungsstruktur bei dem Testprojekt sieht folgendermaßen aus:
|
Bild 0-3: Vererbungsstruktur bei dem Testprojekt mit internem Fenster
- In der main-Methode werden nun die Eingabe-Objekte in das interne Fenster gelegt und das interne Fenster in das Hauptfenster:
|
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.beans.*;
public class Main
{
public static void main(String[] args)
{
Fenster hauptfenster = new Fenster();
hauptfenster.setLayout(null);
InternesFenster mF = new InternesFenster();
mF.setLayout(new GridLayout(3,1));
DoubleParameterBean meineParametereingabeBean1 = new DoubleParameterBean();
DoubleParameterBean meineParametereingabeBean2 = new DoubleParameterBean();
DoubleParameterBean meineParametereingabeBean3 = new DoubleParameterBean();
meineParametereingabeBean1.setID("1.1");
meineParametereingabeBean2.setID("1.2");
meineParametereingabeBean3.setID("1.3");
mF.getContentPane().add(meineParametereingabeBean1);
mF.getContentPane().add(meineParametereingabeBean2);
mF.getContentPane().add(meineParametereingabeBean3);
mF.pack();
mF.setSize(400,150);
mF.setLocation(10,10);
mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mF.setVisible(true);
hauptfenster.getContentPane().add(mF);
hauptfenster.pack();
hauptfenster.setSize(450,200);
hauptfenster.setLocation(10,10);
hauptfenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
hauptfenster.setVisible(true);
meineParametereingabeBean1.setWert(1.0);
meineParametereingabeBean1.setBezeichnung("Wert1:");
meineParametereingabeBean2.setWert(2.0);
meineParametereingabeBean2.setBezeichnung("Wert2:");
meineParametereingabeBean3.setWert(3.0);
meineParametereingabeBean3.setBezeichnung("Wert1+Wert2:");
}
}
Code 0-1: Quelltext zu Main.java für eine Test-Anwendung mit internem Fenster.
bean008a_Doubleeingabe_und_InternesFenster.zip - Download der Testanwendung
Erstellen eines Java-Bean-Archives mit InternesFenster und DoubleParameterBean
- Neben dem Archiv soll hier auch gleich eine API mit javadoc erstellt werden.
- Dies ist ein Programm, mit dem Java-Klassen automatisch dokumentiert werden können.
- Dazu werden alle Methoden und Attribute im Quellcode analysiert, sowie Vererbungs- und Schnittstellenstruktur.
- Kommentare der Form /** Kommetartext */ werden in der Dokumentation zudem automatisch zu den Klassen, Attributen und Methoden hinzugefügt, vor denen sie unmittelbar stehen.
- Ab jetzt soll auch jede Klasse explizit mit einer GPL-Lizenz versehen werden.
- Eine entsprechende Erklärung auf der Portal-Startseite kramann.info gibt es bereits.
- Außerdem ist eine Beschreibungsklasse InternesFensterBeanInfo für InternesFenster zu erstellen und ein Icon.
- Die Verzeichnisstruktur zur Vorbereitung des Archiv-Verzeichnisses und der automatischen Dokumentation sieht vor Aufruf von ANT folgendermaßen aus:
|
Bild 0-4: Verzeichnisstruktur zur Vorbereitung von Archiv-Verzeichnis und Dokumentation.
- In build.xml wird nun angewiesen, dass sowohl DoubleParameterBean als auch InternesFenster und deren Basisklassen gepackt werden sollen und javadoc aufgerufen wird:
|
<?xml version="1.0" encoding="ISO-8859-1"?>
<project default="build">
<dirname property="basedir" file=""/>
<property name="beanname1" value="DoubleParameterBean"/>
<property name="beanname2" value="InternesFenster"/>
<property name="archivname" value="mechabeans"/>
<property name="jarfile" value="/.jar"/>
<target name="build" depends="compile">
<jar destfile="" basedir="" includes="*.class,*.png">
<manifest>
<section name=".class">
<attribute name="Java-Bean" value="true"/>
</section>
<section name=".class">
<attribute name="Java-Bean" value="true"/>
</section>
</manifest>
</jar>
</target>
<target name="compile">
<javac destdir="">
<src location=""/>
</javac>
</target>
<mkdir dir="/javadoc"/>
<javadoc
sourcepath=""
sourcefiles="BasisBean.java,DoubleParameterBean.java,DoubleParameterBeanBeanInfo.java,BasisInternesFenster.java,InternesFenster.java,InternesFensterBeanInfo.java"
destdir="/javadoc"
private="false"
windowtitle=""
doctitle=""
/>
<zip destfile="/-doc.zip" basedir="/javadoc"/>
<target name="clean">
<delete file="">
<fileset dir="" includes="*.class,*.png"/>
</delete>
</target>
</project>
Code 0-2: build.xml für das neue Vorhaben.
- Das nach Aufruf von ant resultierende Verzeichnis kann nachfolgend heruntergeladen werden:
|
bean008b_NetBean_Archiv_mit_GPL_a.zip - In NetBeans-IDE einbinden: mechabeans.jar und mechabeans-doc.zip.
- Die Funktionalität aus propertyChange(PropertyChangeEvent evt) in InternesFenster wurde herausgenommen: Sie wird abhängig von den Projektanforderungen innerhalb von NetBeans-IDE ergänzt:
|
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.beans.*;
/**
* <h4>InternesFenster.java - A bean to be placed in Frames and which registers itself as listener in Java-Beans placed in it.</h4>
* <br/>
* Copyright (C) 2011 Guido Kramann<br/>
* kramann@fh-brandenburg.de<br/>
* http://www.kramann.info<br/>
* <br/>
* <p>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* </p>
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* </p>
* <p>
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* </p>
*/
public class InternesFenster extends BasisInternesFenster
{
/**<h4>In dieser Methode werden PropertyChange-Events verarbeitet, die durch eingefügte Komponenten ausgelöst werden.
Diese Methode muss vervollständigt werden, um Funktionalitäten zu realisieren.</h4>
Dies ist allerdings während des Arbeitens in NetBeans-IDE nicht möglich, denn hier
ist InternesFenster eine nicht veränderbare Java-Bean.
Statt dessen muß die Methode das PropertyChangeEvent weiter nach oben reichen,
damit es im Hauptfenster verarbeitet werden kann:
*/
public void propertyChange(PropertyChangeEvent evt)
{
System.out.println("Ereignisname:"+evt.getPropertyName());
firePropertyChange(evt.getPropertyName(),evt.getOldValue(),evt.getNewValue());
}
}
Code 0-3: Java-Quelltextdatei InternesFenster.java (In den Dokumentations-Kommentaren kann HTML verwendet werden.)
Arbeiten mit InternesFenster und DoubleParameterBean in NetBeans-IDE
- Im Projektbrowserfenster über Libraries des aktuellen Projektes mit rechter Maustaste "Add Library" auswählen und dann über Manage Libraries einen neuen Librarynamen, z.B. "mechabeans" anlegen:
|
Bild 0-5: Einfügen eines neuen Library-Namens (frei gewählt).
- Dann werden das Jar-File mechabeans.jar und das javadoc-Archiv mechabeans-doc.zip der Library hinzugefügt.
|
Bild 0-6: Archiv-File mit den Klassen hinzufügen.
Bild 0-7: Javadoc-Archiv mit Beschreibungen und API der Klassen hinzufügen.
- Blick auf die nun neu verfügbaren Recourcen:
|
Bild 0-8: Neu verfügbare Library und neuer Hilfe-Eintrag.
- Nach Anlegen eines JFrame-Forms werden InternesFenster und DoubleParameterBean über die .jar-Datei mechabeans.jar mit dem Palette Manager in die Kategorie Beans der Palette integriert:
|
Bild 0-9: Nach Auswahl von "Einfügen über JAR" und Übergabe des Archivs mechabeans.jar werden InternesFenster und DoubleParameterBean ausgewählt.
Bild 0-10: Paletteneintrag.
- Als Trägerelement wird nun ein Tabbed Pane und da hinein drei InternesFenster-Objekte gezogen.
- Es müssen danach drei Karteireiter entstanden sein mit je einem Objekt InternesFenster innen.
- Klappt die Markierung des Zielelements vor dem Drag & Drop nicht, kann nach Markieren des Tabbed Pane auch über die rechte Maustaste ausgewählt werden: "Add from Palette -> Beans -> InternesFenster".
- Das Resultat sollte folgendermaßen aussehen:
|
Bild 0-11: Tabbed Pane mit drei Reitern, von dem jeder ein InternesFenster-Objekt enthält.
- Nur den zweiten Tab wollen wir verwenden und ziehen dort drei DoubleParameterBean hinein:
- IDs und Bezeichnungen werden wie im vorangegangenen Beispiel angepaßt.
- #img.png
- Anpassen der Attributbelegungen.
|
- Bei dem Objekt internesFenster2 im mittleren Tab wird nun die Methode propertyChange(PropertyChangeEvent evt) ausgewählt, indem das entsprechende Event ausgewählt wird:
|
Bild 0-12: Auswahl des Events propertyChange für internesFenster2.
- Da die neu erstellte Methode zum JFrame-Form, also zum "obersten Fenster" gehört, können die Methoden in InternesFenster nur aufgerufen werden, indem explizit dieses Objekt angegeben wird:
- Damit diese Methode überhaupt anspricht, reicht die propertyChange-Methode in InternesFenster auftretende Events mit firePropertyChange(..) weiter nach oben.
|
Bild 0-13: Vervollständigen der automatisch erstellten Methode internesFenster2PropertyChange(java.beans.PropertyChangeEvent evt).
Bild 0-14: Test des Programms
bean008d_NetBeans_Projekt_mit_Archiv.zip - Obiges Projekt für NetBeans-IDE.
Fazit
- Die automatische Anmeldung der InternesFenster-Objekte bei allen in sie hineingezogenen Komponenten funktioniert und es ist auch möglich diese später über getComponentByID(...) auszulesen.
- Da aber sowieso eine Listener-Methode im Hauptfenster erstellt werden muß, um innerhalb von NetBeans-IDE Funktionalität anpassen zu können und sowieso alle verwendeten Komponenten auf der Ebene des Hauptfensters eine Referenz besitze, ist der hier beschrittene Weg zunächst umständlich.
|
Übung an dieser Stelle:
- Realisieren Sie die gleiche Funktionalität, indem Sie in internesFenster2PropertyChange(..) nur Referenzen aus dem Hauptfenster benutzen.
|
- Liegt jedoch die Reaktion des InternesFenster-Objektes fest, so kann sie auch direkt in der Java-Bean vorimplementiert werden und ist dann unmittelbar nach dem reinziehen einer Komponente in dieses Objekt aktiv.
- Möglich ist das z.B. wenn DoubleParameterBean über firePropertyChange("wert",wert_alt,this.wert); nicht "wert" als Namen schickt, sondern der Variablenname einer Zielvariable in einer anderen Komponente.
- InternesFenster-Objekte könnten diese Übereinstimmung erfassen und den Wert automatisch weiterleiten.
|
Übung
- Versuchen Sie genau das für die Summieranwendung hinzubekommen.
- Führen Sie dazu z.B. ein neues Attribut "ziel" ein.
- Ist für ID 1.1 dort dann z.B. "1.3.wert" gespeichert, so leitet das InternesFenster-Objekt den wert von 1.1 nach 1.3
|
Introspektion
- Vollautomatisch wird sich diese Aufgabe nur mit Hilfe der Reflection-Library lösen lassen.
- Damit kann aus einem Objekt der Klassenname und die darin verfügbaren Methoden ausgelesen und auch aufgerufen werden.
|
- Folgender Codeausschnitt beispielsweise untersucht Objekte in einer Baumstruktur.
- Besitzt die Klasse eines Objektes dort eine Methode mit einem gewünschten Namen, so wird sie über den Methodennamen automatisch aufgerufen:
|
...
if(zweig.unterobjekt!=null)
{
for(Method method : zweig.unterobjekt.getClass().getMethods() )
{
//System.out.println("MMMMethode von Textfeld:"+method.getName());
if(method.getName().equals(methodenname))
{
//Methode aufrufen:
try
{
ergebnis.append( method.invoke(zweig.unterobjekt,parameter) );
}
catch(Exception e)
{
System.out.println("Es ist ein Fehler aufgetreten in der Klasse Zweig:"+e);
}
}
}
}
...
Code 0-4: Beispiel für Introspektion mit Reflection.
Erweiterungen