kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




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.
Screenshot einer mit NetBeans-IDE realisierten Testanwendung mit Karteikarten (JTabbedPanes).

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.
Screenshot der zum Test gestarteten Anwendung mit internem Fenster.

Bild 0-2: Screenshot der zum Test gestarteten Anwendung mit internem Fenster.

  • Die neue Vererbungsstruktur bei dem Testprojekt sieht folgendermaßen aus:
Vererbungsstruktur bei dem Testprojekt mit internem Fenster

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:
Verzeichnisstruktur zur Vorbereitung von Archiv-Verzeichnis und Dokumentation.

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:
Einfügen eines neuen Library-Namens (frei gewählt).

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.
Archiv-File mit den Klassen hinzufügen.

Bild 0-6: Archiv-File mit den Klassen hinzufügen.

Javadoc-Archiv mit Beschreibungen und API der Klassen hinzufügen.

Bild 0-7: Javadoc-Archiv mit Beschreibungen und API der Klassen hinzufügen.

  • Blick auf die nun neu verfügbaren Recourcen:
Neu verfügbare Library und neuer Hilfe-Eintrag.

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:
Nach Auswahl von

Bild 0-9: Nach Auswahl von "Einfügen über JAR" und Übergabe des Archivs mechabeans.jar werden InternesFenster und DoubleParameterBean ausgewählt.

Paletteneintrag.

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:
Tabbed Pane mit drei Reitern, von dem jeder ein InternesFenster-Objekt enthält.

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:
Auswahl des Events propertyChange für internesFenster2.

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.
Vervollständigen der automatisch erstellten Methode internesFenster2PropertyChange(java.beans.PropertyChangeEvent evt).

Bild 0-13: Vervollständigen der automatisch erstellten Methode internesFenster2PropertyChange(java.beans.PropertyChangeEvent evt).

Test des Programms

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

  • Für NetBeans-IDE gibt es eine Vielzahl an Plugins und Erweiterungen unter http://plugins.netbeans.org.
  • Für unsere Zwecke wären weitere GUI-Elemente nützlich, z.B. eines mit dem sich Bilder direkt einfügen lassen.
  • openswing und swingx bieten dies.
  • Die Installation in NetBeans-IDE erfolgt so ähnlich, wie das Einfügen einer Archivdatei mit Java-Beans:
  • Zuerst müssen Klassen von denen die Java-Beans abhängig sind als Library dem Projekt hinzugefügt werden, dann wernden die zusätzlichen GUI-Elemente über den Palette-Manager in die Palette eingefügt.
  • openswing erhält man hier: http://switch.dl.sourceforge.net/project/oswing.
  • swingx findet man hier: http://swingx.java.net/files/documents/2981/153005/swingx-1.6.2-bundle.zip.