kramann.info
© Guido Kramann

Login: Passwort:










12.1 Erstellen einer einfachen JavaBean

  • Das im folgenden beschriebene iterativ erweiterte Projekt finden Sie in der nachfolgenden .zip-Datei:
Herunterladen der Datei javabean_projekt01.zip.
proj006c_EventTest_Schieber.zip - Musterlösung zur Übung.

Eine Java-Bean muß die Schnittstelle "Serializable" implementieren, damit aktuelle Einstellungen der Attribute eines Objektes der Klasse auf einfache Weise dauerhaft gespeichert werden können. Es macht Sinn ein Java-Bean von einem GUI-Element abzuleiten, damit es später über die Entwicklungsumgebung mit Drag and Drop in eine Oberfläche eingebaut werden kann.

Als Beispiel wird in mehreren Schritte ein Textfenster als Java-Bean entwickelt. Bevor es als Java-Bean in der Entwicklungsumgebung verwendet wird, wird es auch durch ein einfaches Testprogramm überprüft. Das Projekt findet sich im Ordner proj006a_Parameterfeld und besteht aus den Quelltextdateien ParametereingabeBean.java (dies wird die Java-Bean) und TestParametereingabeBean.java (erzeugt eine einfache Oberfläche zum Testen von ParametereingabeBean).

Projekt proj006a_Parameterfeld

import java.awt.Color;
import java.beans.XMLDecoder;
import java.awt.*;
import javax.swing.*;
import java.io.Serializable;
public class ParametereingabeBean extends JPanel implements Serializable 
{
    private JLabel     aufkleber;
    private JTextField textfeld;
    /**
    Für die einzustellenden Parameter müssen get- und set-Methoden bereitgestellt werden.
    */
    private double wert;
    private String bezeichnung;
    /**
    JavaBeans müssen einen leeren Kontruktor besitzen.
    */
    public ParametereingabeBean() 
    {
        wert        = 0.0;
        bezeichnung = "Zahl";        
        textfeld  = new JTextField(""+wert);
        aufkleber = new JLabel(bezeichnung); 
        setLayout(new GridLayout(1,2));
        setBounds(10,60,300,30);
        add(aufkleber);
        add(textfeld);
        setBackground( Color.BLUE );
        aufkleber.setForeground( Color.WHITE );
    } 
}

Code 12.1-1: Projekt proj006a_Parameterfeld, Java-Klasse ParametereingabeBean (Java-Bean) in der Datei ParametereingabeBean.java

Erster Test für ein Element zur Parametereingabe (javac *.java, anschließend: Java TestParametereingabeBean).

Bild 12.1-1: Erster Test für ein Element zur Parametereingabe (javac *.java, anschließend: Java TestParametereingabeBean).

  • Die Klasse "ParametereingabeBean" erbt vom GUI-Element JPanel und fügt diesem Panel
  • zwei Elemente hinzu: einen "Aufkleber" (JLabel) und ein Textfeld (JTextField).
  • Sie soll dazu dienen Parameter über eine Oberfläche eingeben zu können, die mit einem
  • Namen versehen sind.
  • Die folgende Klasse erzeugt ein Fenster und fügt diesem ein Objekt vom Typ
  • "ParametereingabeBean" hinzu:
import java.applet.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
public class TestParametereingabeBean
{
    public static void main(String[] args)
    {
        JFrame mF = new JFrame("TestParametereingabeBean");
        mF.setLayout(null);
        mF.getContentPane().add(new ParametereingabeBean());
        mF.pack();
        mF.setSize(500,500);
        mF.setLocation(10,10);
        mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mF.setVisible(true);        
    }
}

Code 12.1-2: Projekt proj006a_Parameterfeld, Java-Klasse TestParametereingabeBean in der Datei TestParametereingabeBean.java

proj006b_GetterSetter

Damit den für Java-Beans bestehenden Konventionen Genüge getan wird, muß für jedes Attribut, für das die Entwicklungsumgebung automatisch ein Parametereingabefeld erzeugen soll eine get- und eine set-Methode geschrieben werden:

public class ParametereingabeBean extends JPanel implements Serializable 
{
    private JLabel     aufkleber;
    private JTextField textfeld;
    /**
    Für die einzustellenden Parameter müssen get- und set-Methoden bereitgestellt werden.
    */
    private double wert;
    private String bezeichnung;
    private int breite;
    private int hoehe;
    public void setWert(double wert) 
    {
        textfeld.setText(""+wert);
        this.wert=wert;
    }
    public double getWert() 
    {
        try
        {
            double x = Double.parseDouble(textfeld.getText());
            this.wert = x;
        }
        catch(Exception e)
        {
        }
        return this.wert;
    }
    public void setBezeichnung(String bezeichnung) 
    {
        aufkleber.setText(bezeichnung);
        this.bezeichnung=bezeichnung;
    }
    public String getBezeichnung() 
    {
        return this.bezeichnung;
    }
    public void setBreite(int breite)
    {
        Dimension dim = this.getSize();
        this.setSize(breite,(int)dim.getHeight());
        this.breite = breite;
    }
    public int getBreite()
    {
        return this.breite;
    }
    public void setHoehe(int hoehe)
    {
        Dimension dim = this.getSize();
        this.setSize((int)dim.getWidth(),hoehe);
        this.hoehe = hoehe;
    }
    public int getHoehe()
    {
        return this.hoehe;
    }
    /**
    JavaBeans müssen einen leeren Kontruktor besitzen.
    */
    public ParametereingabeBean() 
    {
        wert        = 0.0;
        bezeichnung = "Zahl";        
        textfeld  = new JTextField(""+wert);
        aufkleber = new JLabel(bezeichnung); 
        setLayout(new GridLayout(1,2));
        breite = 300;
        hoehe  = 30;
        setBounds(10,60,breite,hoehe);
        add(aufkleber);
        add(textfeld);
        setBackground( Color.BLUE );
        aufkleber.setForeground( Color.WHITE );
    } 
}

Code 12.1-3: proj006b_GetterSetter, Java-Klasse ParametereingabeBean (Java-Bean) in der Datei ParametereingabeBean.java

Um die Einstellmöglichkeiten zu testen, wird auch die Testklasse erweitert:

import java.applet.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
public class TestParametereingabeBean
{
    public static void main(String[] args)
    {
        JFrame mF = new JFrame("TestParametereingabeBean");
        mF.setLayout(null);
        ParametereingabeBean meineParametereingabeBean = new ParametereingabeBean();
        mF.getContentPane().add(meineParametereingabeBean);
        mF.pack();
        mF.setSize(500,500);
        mF.setLocation(10,10);
        mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mF.setVisible(true);
        meineParametereingabeBean.setWert(3.5);
        meineParametereingabeBean.setBezeichnung("Wert:");
        meineParametereingabeBean.setBreite(500);
        meineParametereingabeBean.setHoehe(50);
        System.out.println("Aktuelle Bezeichnung: "+meineParametereingabeBean.getBezeichnung());
        System.out.println("Aktueller Wert: "+meineParametereingabeBean.getWert());
    }
}

Code 12.1-4: proj006b_GetterSetter, Java-Klasse TestParametereingabeBean in der Datei TestParametereingabeBean.java

Ergenis der Änderungen der Objekteigenschaften über die set-Methoden.

Bild 12.1-2: Ergenis der Änderungen der Objekteigenschaften über die set-Methoden.

proj006c_EventTest

Nun sollen drei Parameter-Eingabe-Objekte erstellt werden und im dritten immer die Summe der ersten beiden erscheinen.

Programm, um die Summe zweier Anzeigefelder zu bilden.

Bild 12.1-3: Programm, um die Summe zweier Anzeigefelder zu bilden.

Bei der Realisierung müssen folgende Probleme gelöst werden:

  • Es wird eine Listener-Klasse geben (EreignisseVerarbeiten), die erkennt, wenn ein Text in den ersten beiden Feldern geändert wurde und dann im dritten die richte Ausgabe herausschreibt.
  • Es ist zwar möglich das in jedes Parameter-Eingabe-Objekt eingebettete Textfeld dazu zu bringen, bei jeder Textänderung ein Signal an ein registriertes Lauscher-Objekt zu liefern, jedoch sind sie mit private-Modifikator eingebettet in die Parameter-Eingabe-Objekte und können nur sehr umständlich direkt abgefragt werden.
  • Aus diesem Grund veranlassen die Textobjekte ihr übergeordnetes Parameter-Eingabe-Objekt dazu seinerseits eine Zustandsänderung zu signalisieren.
  • Dies wird realisiert, indem processEvent(AWTEvent e) im Textfeld setWertIntern(String text) in ParametereingabeBean aufruft und diese wiederum firePropertyChange("wert",wert_alt,wert)

Dabei ist firePropertyChange("wert",wert_alt,wert) eine spezielle Bean-Methode, die sowieso für die Kommunikation von Beans untereinander bestimmt ist.

  • Ein Objekt der Klasse EreignisseVerarbeiten wird deshalb bei den drei Objekten vom Typ ParametereingabeBean registriert.
  • propertyChange(PropertyChangeEvent evt) in EreignisseVerarbeiten wird genau dann aufgerufen, wenn eine Engabetextänderung stattfand.
  • Hinweis: Damit unvollständige oder unsinnige Einträge in den Textfeldern keine Wirkung haben, werden diese über einen try/catch-Block, in dem das Parsen erfolgt abgefangen.

Es folgen die Quelltexte:

import java.awt.Color;
import java.beans.XMLDecoder;
import java.awt.*;
import javax.swing.*;
import java.io.Serializable;
public class ParametereingabeBean extends JPanel implements Serializable 
{
    private JLabel     aufkleber;
    private Textfeld textfeld;
    /**
    Für die einzustellenden Parameter müssen get- und set-Methoden bereitgestellt werden.
    */
    private double wert;
    private String bezeichnung;
    private int breite;
    private int hoehe;
    public void setWert(double wert) 
    {
        textfeld.setText(""+wert);
        this.wert=wert;
    }
    public double getWert() 
    {
        try
        {
            double x = Double.parseDouble(textfeld.getText());
            this.wert = x;
        }
        catch(Exception e)
        {
        }
        return this.wert;
    }
    public void setBezeichnung(String bezeichnung) 
    {
        aufkleber.setText(bezeichnung);
        this.bezeichnung=bezeichnung;
    }
    public String getBezeichnung() 
    {
        return this.bezeichnung;
    }
    public void setBreite(int breite)
    {
        Dimension dim = this.getSize();
        this.setSize(breite,(int)dim.getHeight());
        this.breite = breite;
    }
    public int getBreite()
    {
        return this.breite;
    }
    public void setHoehe(int hoehe)
    {
        Dimension dim = this.getSize();
        this.setSize((int)dim.getWidth(),hoehe);
        this.hoehe = hoehe;
    }
    public int getHoehe()
    {
        return this.hoehe;
    }
    public void setWertIntern(String text)
    {
        double x = wert;
        double wert_alt = wert;
        boolean ok = true;
        try
        {
            x = Double.parseDouble(text);
        }
        catch(Exception e)
        {
            ok = false;
        }
        if( ok==true )
            wert = x;
        firePropertyChange("wert",wert_alt,wert);
    }
    /**
    JavaBeans müssen einen leeren Kontruktor besitzen.
    */
    public ParametereingabeBean() 
    {
        wert        = 0.0;
        bezeichnung = "Zahl";        
        textfeld  = new Textfeld(this,""+wert);
        aufkleber = new JLabel(bezeichnung); 
        setLayout(new GridLayout(1,2));
        breite = 300;
        hoehe  = 30;
        setBounds(10,60,breite,hoehe);
        add(aufkleber);
        add(textfeld);
        setBackground( Color.BLUE );
        aufkleber.setForeground( Color.WHITE );
    } 
}

Code 12.1-5: proj006c_EventTest, Java-Klasse ParametereingabeBean (Java-Bean) in der Datei ParametereingabeBean.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
public class EreignisseVerarbeiten implements PropertyChangeListener
{
    private ParametereingabeBean pb1;
    private ParametereingabeBean pb2;
    private ParametereingabeBean pb3;
    EreignisseVerarbeiten(ParametereingabeBean pb1,
                          ParametereingabeBean pb2,
                          ParametereingabeBean pb3)
    {
        this.pb1 = pb1;
        this.pb2 = pb2;
        this.pb3 = pb3;
    }
    public void propertyChange(PropertyChangeEvent evt) 
    {
        System.out.println("Text geändert");
        pb3.setWert(pb1.getWert()+pb2.getWert());
    }
}

Code 12.1-6: proj006c_EventTest, Java-Klasse EreignisseVerarbeiten in der Datei EreignisseVerarbeiten.java

  • Um das eingebettete Textfeld beim Erstellen für das Senden von Änderungsmitteilungen zu aktivieren und
  • die Methode processEvent(AWTEvent e) überschreiben zu können, wird nicht mehr die Standard-Klasse
  • JTextField verwendet, sondern eine selbst geschriebene von TextField erbende:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.beans.*;
public class Textfeld extends TextField
{
    private ParametereingabeBean pb;
    public void aktivieren()
    {
        enableEvents (AWTEvent.ACTION_EVENT_MASK);             
        enableEvents (AWTEvent.TEXT_EVENT_MASK);             
        enableEvents (AWTEvent.FOCUS_EVENT_MASK);             
    }
    Textfeld(ParametereingabeBean pb,String text)
    {
        super(text);
        aktivieren();
        this.pb = pb;       
    }
    protected void processEvent(AWTEvent e) 
    { 
         pb.setWertIntern(getText());
    } 
} 

Code 12.1-7: proj006c_EventTest, Java-Klasse Textfeld in der Datei Textfeld.java

An der main-Methode im Quelltext TestParametereingabeBean.java ist zu ersehen, wie die Objekte miteinander verschaltet werden: Alle drei Eingabefelder meineParametereingabeBean1..3 werden in ev registriert. Dies erfolgt durch Übergabe an den Konstruktor und darin durch Speichern der übergebenen Referenzen als Attribute. Als zweites wird der Listener ev (vom Typ EreignisseVerarbeiten) bei allen drei ParametereingabeBean-Objekten über addPropertyChangeListener(ev) registriert. Dadurch reagiert dann die Methode propertyChange(PropertyChangeEvent evt) in ev auf jede Textänderung in jedem Parameter-Eingabe-Objekt.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.beans.*;


public class TestParametereingabeBean
{
    public static void main(String[] args)
    {
        JFrame mF = new JFrame("TestParametereingabeBean");
        mF.setLayout(new GridLayout(3,1));
        ParametereingabeBean meineParametereingabeBean1 = new ParametereingabeBean();
        ParametereingabeBean meineParametereingabeBean2 = new ParametereingabeBean();
        ParametereingabeBean meineParametereingabeBean3 = new ParametereingabeBean();
        mF.getContentPane().add(meineParametereingabeBean1);
        mF.getContentPane().add(meineParametereingabeBean2);
        mF.getContentPane().add(meineParametereingabeBean3);
        mF.pack();
        mF.setSize(500,100);
        mF.setLocation(10,10);
        mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mF.setVisible(true);
        
        EreignisseVerarbeiten ev = new EreignisseVerarbeiten(meineParametereingabeBean1,
                                                             meineParametereingabeBean2,
                                                             meineParametereingabeBean3);

        meineParametereingabeBean1.addPropertyChangeListener(ev);
        meineParametereingabeBean2.addPropertyChangeListener(ev);
        meineParametereingabeBean3.addPropertyChangeListener(ev);

        meineParametereingabeBean1.setWert(1.0);
        meineParametereingabeBean1.setBezeichnung("Wert1:");
        meineParametereingabeBean2.setWert(2.0);
        meineParametereingabeBean2.setBezeichnung("Wert2:");
        meineParametereingabeBean3.setWert(3.0);
        meineParametereingabeBean3.setBezeichnung("Wert1+Wert2:");
    }
}

Code 12.1-8: proj006c_EventTest, Java-Klasse TestParametereingabeBean in der Datei TestParametereingabeBean.java

Das Projekt proj006d_InnereKlasse entspricht weitestgehend proj006c_EventTest. Allerdings wurde hier die Klasse Textfeld als innere Klasse von ParametereingabeBean umgesetzt. Dies ermöglicht dem Textfeld-Objekt den direkten Zugriff auf die Methode setWertIntern(...) des übergeordneten Objektes vom Typ ParametereingabeBean, ohne dass dieses extra beim Textfeld-Objekt registriert werden müßte. /p>

Bisher wurde eine Testanwendung für ParametereingabeBean umgesetzt. Zukünftig soll ParametereingabeBean als Drag and Drop-Element in der Entwicklungsumgebung NetBeansIDE eingebunden werden. Vorbereitend darauf muß die Klasse ParametereingabeBean in eine gepackte Datei vom Typ .jar umgewandelt werden. Dies geht am einfachsten mit der Skriptsprache ANT unter Verwendung eines XML-Skriptes mit dem Namen build.xml. Die Umwandlung erfolgt, wenn ParametereingabeBean.java und build.xml im gleichen Verzeichnis liegen und dort einfach "ant" von der Konsole aus aufgerufen wird. Sowohl das Kompilieren, als auch die Erstellung der .jar-Datei wird dann von ANT übernommen. Andere Klassen können leicht genauso behandelt werden. Lediglich der Eintrag des Klassennamens in der build.xml-Datei muß hierfür angepaßt werden. Im folgenden ist der Inhalt von build.xml zu sehen: /p>

<?xml version="1.0" encoding="ISO-8859-1"?>

    <project default="build">

        <dirname property="basedir" file=""/>
        <property name="beanname" value="ParametereingabeBean"/>
        <property name="jarfile" value="/.jar"/>

        <target name="build" depends="compile">
              <jar destfile="" basedir="" includes="*.class">
                  <manifest>
                      <section name=".class">
                          <attribute name="Java-Bean" value="true"/>
                      </section>
                  </manifest>
              </jar>
        </target>

        <target name="compile">
            <javac destdir="">
                <src location=""/>
            </javac>
        </target>

        <target name="clean">
            <delete file="">
                <fileset dir="" includes="*.class"/>
            </delete>
        </target>

    </project> 

Code 12.1-9: proj006e_JavaBean, build.xml

Übung
  • Bringen Sie ein vergleichbares Programm genauso weit.
  • Dieses soll aber auf Schiebereglern beruhen.
  • D.h. es soll am Ende drei Schieberegler geben, wobei der dritte die Summe der Einstellungen der ersten beiden einnimmt.