Warning: array_merge() [function.array-merge]: Argument #1 is not an array in /home/www/web165/html/silverstrike/lib/plugins/meta/syntax.php on line 91

Wie Sie mit SwiXML Zeit bei der Entwicklung von Java-GUIs sparen können

In diesem Artikel werden Sie erfahren, wie Sie

  • Zeit und Geld bei der Entwicklung von Java-Benutzerschnittstellen (GUIs) sparen und
  • die Wartbarkeit Ihrer Software verbessern

können.

Das Problem

Stellen Sie sich vor, Sie wollen eine Dialogbox erstellen, die folgendermassen aussieht:

Fenster, welches in der Klasse SampleFormJava erzeugt wird.

Wie sieht der Java-Source-Code aus, den man benötigt, um eine solche Dialogbox anzuzeigen?

Eine mögliche Lösung ist folgende:

/**
 * Copyright (c) Dmitri Pissarenko
 * http://sw-dev.at
 */
package at.swDev.swixml;
 
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
 
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import javax.swing.border.TitledBorder;
 
/**
 * This class is used for demonstrating how a simple form can be programmed in
 * Java.
 *
 * @author Dmitri Pissarenko
 */
public class SampleFormJava extends JFrame {
    /**
     * Grid width of button panel (OK, Cancel). Necessary for laying out with
     * GridBagLayout.
     */
    private static final int OK_CANCEL_BUTTON_PANEL_GRIDWIDTH = 2;
 
    /**
     * Y coordinate of button panel (OK, Cancel). Necessary for laying out with
     * GridBagLayout.
     */
    private static final int OK_CANCEL_BUTTON_PANEL_GRID_Y = 6;
 
    /**
     * X coordinate of button panel (OK, Cancel). Necessary for laying out with
     * GridBagLayout.
     */
    private static final int OK_CANCEL_BUTTON_PANEL_GRID_X = 0;
 
    /**
     * Grid width of "subscribe to course" checkbox. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int SUBSCRIBE_CHECKBOX_GRIDWIDTH = 2;
 
    /**
     * Y coordinate of "subscribe to course" checkbox. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int SUBSCRIBE_CHECKBOX_GRID_Y = 5;
 
    /**
     * X coordinate of "subscribe to course" checkbox. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int SUBSCRIBE_CHECKBOX_GRID_X = 0;
 
    /**
     * Y coordinate of "e-mail" text field. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int EMAIL_TEXT_FIELD_GRID_Y = 4;
 
    /**
     * X coordinate of "e-mail" text field. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int EMAIL_TEXT_FIELD_GRID_X = 1;
 
    /**
     * Y coordinate of "e-mail" label. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int EMAIL_LABEL_GRID_Y = 4;
 
    /**
     * X coordinate of "e-mail" label. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int EMAIL_LABEL_GRID_X = 0;
 
    /**
     * Y coordinate of "country" combo box. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int COUNTRY_COMBO_GRID_Y = 3;
 
    /**
     * X coordinate of "country" combo box. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int COUNTRY_COMBO_GRID_X = 1;
 
    /**
     * Y coordinate of "country" label. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int COUNTRY_LABEL_GRID_Y = 3;
 
    /**
     * X coordinate of "country" label. Necessary for laying out
     * with GridBagLayout.
     */
    private static final int COUNTRY_LABEL_GRID_X = 0;
 
    /**
     * This constant is used for initializing column count in text fields.
     */
    private static final int FIFTY_CHARACTERS = 50;
 
    /**
     * This constant is used for initializing column count in text fields.
     */
    private static final int FORTY_CHARACTERS = 40;
 
    /**
     * This constant represents insets object with a distance of 5 pixels on
     * each side.
     */
    private static final Insets FIVE_PIXEL_INSETS = new Insets(5, 5, 5, 5);
 
    /**
     * Default serial version ID.
     */
    private static final long serialVersionUID = 1L;
 
    /**
     * Layout manager used in the form.
     */
    private GridBagLayout layout;
 
    /**
     * Text field for entering first name.
     */
    private JTextField firstNameTextField;
 
    /**
     * Text field for entering surname.
     */
    private JTextField surNameTextField;
 
    /**
     * Radio button "male" for selecting gender.
     */
    private JRadioButton maleRadioButton;
 
    /**
     * Radio button "female" for selecting gender.
     */
    private JRadioButton femaleRadioButton;
 
    /**
     * Combo box for selecting country.
     */
    private JComboBox countryComboBox;
 
    /**
     * Text field for entering e-mail.
     */
    private JTextField emailTextField;
 
    /**
     * Check box "subscribe to course".
     */
    private JCheckBox subscribeCheckBox;
 
    /**
     * Constructor. Creates the form, fills "country" combo box with values and
     * packs the frame.
     */
    public SampleFormJava() {
        super();
 
        // Create user interface elements
        this.setupUI();
 
        // Fill "country" combo box with values
        this.fillComboBox();
 
        this.pack();
    }
 
    /**
     * This method creates all the user interface elements of the form.
     */
    protected final void setupUI() {
        GridBagConstraints constraints = null;
        JLabel firstNameLabel = null;
        JLabel surNameLabel = null;
        JLabel countryLabel = null;
        JLabel emailLabel = null;
        JButton okButton = null;
        JButton cancelButton = null;
        JPanel genderPanel = null;
        JPanel buttonPanel = null;
        ButtonGroup genderButtonGroup = null;
 
        layout = new GridBagLayout();
        this.setLayout(layout);
 
        firstNameLabel = new JLabel("First name:");
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(firstNameLabel, constraints);
 
        firstNameTextField = new JTextField();
        firstNameTextField.setColumns(FORTY_CHARACTERS);
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = 1;
        constraints.gridy = 0;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(firstNameTextField, constraints);
 
        surNameLabel = new JLabel("Surname:");
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = 0;
        constraints.gridy = 1;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(surNameLabel, constraints);
 
        surNameTextField = new JTextField();
        surNameTextField.setColumns(FIFTY_CHARACTERS);
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = 1;
        constraints.gridy = 1;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(surNameTextField, constraints);
 
        genderButtonGroup = new ButtonGroup();
        maleRadioButton = new JRadioButton("Male");
        femaleRadioButton = new JRadioButton("Female");
        genderButtonGroup.add(maleRadioButton);
        genderButtonGroup.add(femaleRadioButton);
 
        genderPanel = new JPanel();
        genderPanel.setBorder(new TitledBorder("Gender"));
        genderPanel.add(maleRadioButton);
        genderPanel.add(femaleRadioButton);
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = 0;
        constraints.gridy = 2;
        constraints.gridwidth = 2;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(genderPanel, constraints);
 
        countryLabel = new JLabel("Country");
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = COUNTRY_LABEL_GRID_X;
        constraints.gridy = COUNTRY_LABEL_GRID_Y;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(countryLabel, constraints);
 
        countryComboBox = new JComboBox();
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = COUNTRY_COMBO_GRID_X;
        constraints.gridy = COUNTRY_COMBO_GRID_Y;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(countryComboBox, constraints);
 
        emailLabel = new JLabel("E-Mail:");
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = EMAIL_LABEL_GRID_X;
        constraints.gridy = EMAIL_LABEL_GRID_Y;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(emailLabel, constraints);
 
        emailTextField = new JTextField();
        emailTextField.setColumns(FIFTY_CHARACTERS);
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = EMAIL_TEXT_FIELD_GRID_X;
        constraints.gridy = EMAIL_TEXT_FIELD_GRID_Y;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(emailTextField, constraints);
 
        subscribeCheckBox = new JCheckBox("Subscribe to course");
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridx = SUBSCRIBE_CHECKBOX_GRID_X;
        constraints.gridy = SUBSCRIBE_CHECKBOX_GRID_Y;
        constraints.gridwidth = SUBSCRIBE_CHECKBOX_GRIDWIDTH;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(subscribeCheckBox, constraints);
 
        buttonPanel = new JPanel();
 
        okButton = new JButton("OK");
        cancelButton = new JButton("Cancel");
        buttonPanel.add(okButton);
        buttonPanel.add(cancelButton);
 
        constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.CENTER;
        constraints.gridx = OK_CANCEL_BUTTON_PANEL_GRID_X;
        constraints.gridy = OK_CANCEL_BUTTON_PANEL_GRID_Y;
        constraints.gridwidth = OK_CANCEL_BUTTON_PANEL_GRIDWIDTH;
        constraints.insets = FIVE_PIXEL_INSETS;
        this.add(buttonPanel, constraints);
 
        this.setTitle("\"Efficient Java UI development with "
                + "SwiXML\" by Dmitri Pissarenko");
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.setResizable(false);
    }
 
    /**
     * This method fills the combobox "Country" with values.
     */
    protected final void fillComboBox() {
        this.countryComboBox.addItem("Russia");
        this.countryComboBox.addItem("Austria");
        this.countryComboBox.addItem("Germany");
        this.countryComboBox.addItem("Belarus");
        this.countryComboBox.addItem("Ukraine");
        this.countryComboBox.addItem("Other");
    }
 
    /**
     * This main method displays a sample form with user interface elements like
     * textfield, radio box, combo box, check box and push buttons.
     *
     * @param args The arguments are not used in the program.
     */
    public static void main(final String[] args) {
        SampleFormJava form = null;
 
        form = new SampleFormJava();
        form.setVisible(true);
    }
}

Diese Methode hat jedoch mehrere Nachteile.

Zum einen die Code-Grösse - um ein kleines Formular zu erzeugen, werden 5 Seiten an Source-Code benötigt (hier finden Sie die PDF-Darstellung des Codes, um die Seiten zählen zu können).

Zum anderen, ist der Code unübersichtlich. Stellen Sie sich vor, Sie schreiben dieses Programm jetzt und vergessen darüber für 5 Jahre. Dann, nach 5 Jahren müssen Sie das Formular ändern.

Wieviel Zeit werden Sie benötigen, um herauszufinden, welche Änderungen Sie vornehmen müssen?

Wenn Sie irgendwo in diesen 5 Seiten einen Fehler machen, wieviel Zeit werden Sie benötigen, um den Fehler zu finden (besonders wenn Sie in Eile sind) ?

Im nächsten Abschnitt werden Sie sehen, wie dieses Problem gelöst werden kann.

Die Lösung

Wie können wir das Problem lösen?

Zuerst unterteilen wir den gesamten Code in zwei Teile

 1. Definition der GUI und
 2. alle anderen Aufgaben.

Teil 1 besteht in unserem Fall aus der Methode SampleFormJava.setupUI(). Sie macht den größten Teil des Programms aus.

Wir refakturieren den Code, sodass diese Methode so aussieht:

    /**
     * This method creates a JFrame from the XML UI descriptor.
     */
    protected final void setupUI() {
        try {
            (new SwingEngine(this)).render(this.getClass().getClassLoader()
                    .getResource(UI_DEF_FILE_NAME));
        } catch (Exception exception) {
            LOGGER.log(Level.SEVERE, "An error occured when "
                    + "setting up the user interface.", exception);
        }
    }

Diese Methode tut Folgendes:

  1. Zunächst wird XML-GUI-Deskriptor geladen (der Dateiname steht in der Konstante UI_DEF_FILE_NAME).
  2. Daraufhin erzeugt die Instanz der Klasse SwingEngine eine JFrame aus dem XML-GUI-Deskriptor (siehe oben).
  3. Schliesslich wird die erzeugte JFrame dem Attribut frame zugewiesen. Das passiert deswegen, weil im XML-GUI-Deskriptor (siehe unten) in der ersten Zeile id=„frame“ steht - so weiß SwingEngine, welchem Attribut die erzeugte GUI zugewiesen werden soll.

Ab jetzt kann die erzeugte JFrame auf die gleiche Art und Weise benutzt werden, als wäre sie „normal“ programmiert worden.

Sehen wir uns jetzt den XML-GUI-Deskriptor an:

<?xml version="1.0" encoding="UTF-8"?> 
 
<frame id="frame" title="&quot;Efficient Java UI development with SwiXML&quot; by Dmitri Pissarenko" 
layout="GridBagLayout" resizable="true" 
defaultCloseOperation="WindowConstants.EXIT_ON_CLOSE">
        <label id="firstNameLabel" text="First name:">
                <gridbagconstraints insets="5,5,5,5" 
                        gridx="0" gridy="0" anchor="GridBagConstraints.WEST"/> 
        </label>
        <textfield id="firstNameTextField" columns="40">
                <gridbagconstraints 
                        insets="5,5,5,5" gridx="1" gridy="0" 
                        anchor="GridBagConstraints.WEST"/> 
        </textfield>
 
        <label id="surNameLabel" text="Surname:">
                <gridbagconstraints 
                        insets="5,5,5,5" gridx="0" gridy="1" 
                        anchor="GridBagConstraints.WEST"/> 
        </label>
        <textfield id="surNameTextField" columns="50">
                <gridbagconstraints 
                        insets="5,5,5,5" gridx="1" gridy="1" 
                        anchor="GridBagConstraints.WEST"/> 
        </textfield>
 
        <panel id="genderPanel" border="TitledBorder(Gender)">
                <gridbagconstraints 
                        insets="5,5,5,5" gridx="0" gridy="2" gridwidth="2" 
                        anchor="GridBagConstraints.WEST"/> 
                <buttongroup id="genderButtonGroup">
                        <radiobutton id="maleRadioButton" text="Male"/>
                        <radiobutton id="femaleRadioButton" text="Female"/>                     
                </buttongroup>
        </panel>
 
        <label id="countryLabel" text="Country:">
                <gridbagconstraints insets="5,5,5,5" gridx="0" gridy="3" 
                anchor="GridBagConstraints.WEST"/> 
        </label>
        <combobox id="countryComboBox">
                <gridbagconstraints insets="5,5,5,5" gridx="1" gridy="3" 
                anchor="GridBagConstraints.WEST"/> 
        </combobox>
 
        <label id="emailLabel" text="E-Mail:">
                <gridbagconstraints insets="5,5,5,5" gridx="0" gridy="4" 
                anchor="GridBagConstraints.WEST"/> 
        </label>
        <textfield id="emailTextField" columns="50">
                <gridbagconstraints insets="5,5,5,5" gridx="1" gridy="4" 
                anchor="GridBagConstraints.WEST"/> 
        </textfield>
 
        <checkbox id="subscribeCheckBox" text="Subscribe to course">
                <gridbagconstraints insets="5,5,5,5" gridx="0" gridy="5" 
                gridwidth="2" anchor="GridBagConstraints.WEST"/> 
        </checkbox>
 
        <panel layout="GridBagLayout">
                <gridbagconstraints insets="5,5,5,5" gridx="0" gridy="6" 
                        gridwidth="2" anchor="GridBagConstraints.CENTER"/> 
                <button id="okButton" action="okButtonAction" text="OK">
                        <gridbagconstraints insets="5,5,5,5" gridx="0" gridy="0" 
                                anchor="GridBagConstraints.WEST"/> 
                </button>
                <button id="cancelButton" action="cancelButtonAction" text="Cancel">
                        <gridbagconstraints insets="5,5,5,5" gridx="1" gridy="0" 
                                anchor="GridBagConstraints.WEST"/> 
                </button>
        </panel>
</frame>

Jetzt haben wir das ursprüngliche Programm in zwei Teile zerlegt:

  1. XML-GUI-Deskriptor SampleFormSwiXML.xml.

Beide Dateien sind jeweils 2 Seiten lang. Dadurch sparen wir uns ca. 1 Seite. Das ist aber nicht der wichtigste Vorteil dieser Lösung.

Wir können jetzt den XML-GUI-Deskriptor mit Copy/Paste wiederverwenden - JPanel mit OK- und Cancel-Schaltflächen werden Sie vermutlich des Öfteren benötigen. Auch das ist aber nicht das Wichtigste.

Diese Lösung gibt uns eine Chance, den Code zur Präsentation der Daten (auch View genannt), vom

  1. Datenmodell (Model) und
  2. Programmsteuerung (Controller)

sauber zu trennen (im Sinne des Entwurfsmusters Model-View-Controller).

Welche Vorteile bringt diese Trennung?

Nehmen wir an, Sie wollen das Aussehen der Dialogbox ein wenig ändern - ein Textfeld soll kleiner/grösser gemacht werden, Text in einem Label soll verändert werden und Ähnliches.

Solche Änderungen betreffen nur die Präsentationsschicht. Wenn die Präsentation in einer separaten XML-Datei gekapselt ist, dann brauchen Sie nur diese XML-Datei zu verändern.

Dadurch wissen Sie sofort, wo Sie die Änderungen vornehmen müssen.

Wenn Präsentation, Datenhaltung und Programmsteuerung in einer Java-Datei zusammengewürfelt sind (wie im Beispiel am Beginndieses Artikels), dann müssen Sie zuerst die Stelle finden, an der dieGUI erzeugt wird. Das kann mühsam werden, besonders bei komplexen GUIs.

Weiters brauchen Sie nur zu wissen, wie die GUI aussieht - nicht aber, wie sie mit Datenhaltung und Programmsteuerung verbunden ist. Wenn es keine Trennung zwischen Präsentation, Datenhaltung undProgrammsteuerung gibt, ist es notwendig zu wissen, wie und wo (an welchen Codestellen) sie miteinander verbunden sind - sonst kann man einen Fehler machen.

Last but not least ist die XML-Datei eine Baumstruktur, also etwas Hierarchisches. Wenn man die XML-Datei anschaut, sieht man sofort, dass z. B. maleRadioButton und femaleRadioButton „Kinder“ von genderPanel sind.

Der Java-Code für die Erzeugung derselben GUI ist dagegen linear aufgebaut. Ein Statement folgt dem anderen und es gibt keine Chance, durch Anschauen des Codes herauszufinden, wie die GUI in etwa aussieht.

SwiXML

Die GUIs werden von SwingEngine generiert. Diese ist Teil des Open Source-Produkts SwiXML von Wolf Paulus.

Die Lizenz des SwiXML erlaubt die Verwendung von SwiXML in kommerziellen Anwendungen. Sie können SwiXML also problemlos in Ihre Anwendungen einbauen.

Vorschau von XML-GUI-Deskriptoren mit xml-ui-viewer

Wenn Sie gerade einen XML-GUI-Deskriptor entwickeln und sehen wollen, ob er richtig ist, dann haben Sie zwei Möglichkeiten:

  1. Sie schreiben ein kleines Java-Programm.
  2. Sie machen eine „Vorschau“ der GUI ohne ein Java-Programm zu schreiben.

Wie die erste Option funktioniert, wissen Sie bereits.

Um den zweiten Weg zu gehen, ist es notwendig, folgende Schritte zu machen:

  1. Laden Sie bitte das kostenlose Programm xml-ui-viewer herunter.
  2. Entpacken Sie die ZIP-Datei ein beliebiges Verzeichnis, z. B. C:\xml-ui-viewer.
  3. Öffnen Sie die Kommandozeile und gehen Sie ins Verzeichnis C:\xml-ui-viewer. Dort finden Sie - unter anderem - Verzeichnisse bin, lib und die Dateien xml-ui-viewer.jar und SampleFormSwiXML.xml.
  4. Geben Sie nun

java -jar xml-ui-viewer.jar SampleFormSwiXML.xml
in der Kommandozeile ein.
SampleFormSwiXML.xml ist ein XML-GUI-Deskriptor.

Es wird ein Fenster erscheinen, das so aussieht, wie im Screenshot am Beginn dieses Artikels.

Auf diese Weise können Sie mit wenig Aufwand eine Vorschau Ihrer GUI machen - ohne eine einzige Zeile programmieren zu müssen.

Wie Sie auf SwiXML-generierte Steuerelemente aus Ihrem Programm zugreifen können

Um SwiXML in richtigen Anwendungen einsetzen zu können, müssen wir wissen wie man auf die von SwiXML generierten Steuerelemente zugreifen kann.

Nehmen wir an, wir wollen den Text in einem Textfeld auf einen vorgegebenen Wert setzen. Dieser Wert wird zur Laufzeit bestimmt.

Um dies machen zu können, müssen wir zwei Dinge tun:

  1. Referenz auf das Textfeld holen.
  2. Methode zum Setzen des Textes aufrufen.

Nehmen wir an, wir wollen den Text des Textfeldes firstName in unserer Beispiel-GUI auf einen bestimmten Wert setzen.

Sehen Sie sich zunächst den XML-GUI-Deskriptor an, vor allem jenen Teil, wo firstName-Textfeld definiert wird:

<?xml version="1.0" encoding="UTF-8"?> 
 
<frame id="frame" title="&quot;Efficient Java UI development with SwiXML&quot;
by Dmitri Pissarenko" 
layout="GridBagLayout" resizable="true" 
defaultCloseOperation="WindowConstants.EXIT_ON_CLOSE">
 
...
 
        <textfield id="firstNameTextField" columns="40">
                <gridbagconstraints 
                        insets="5,5,5,5" gridx="1" gridy="0" 
                        anchor="GridBagConstraints.WEST"/> 
        </textfield>
...
</frame>

Wie Sie sehen, hat das Textfeld die ID firstNameTextField.

Um auf dieses Textfeld zugreifen zu können, müssen wir in der Klasse eine Member-Variable mit dem gleichen Namen anlegen:

public class SampleFormSwiXML2
{
...
    private JTextField firstNameTextField;
...
}

Wenn SwiXML die GUI generiert (das passiert beim Aufruf der Methode SwingEngine.render), „verbindet“ es

  1. alle Steuerelemente mit IDs mit
  2. gleichnamigen Member-Variablen der Klasse.

Das bedeutet: Wenn Sie im Java-Programm auf ein Steuerelement zugreifen wollen, müssen Sie zwei Dinge tun:

  1. Dem Steuerelement im XML-GUI-Deskriptor eine ID geben.
  2. In der Klasse mit dem SwingEngine.render-Aufruf eine Member-Variable anlegen, die
    1. genauso heisst, wie die ID und
    2. den richtigen Typ hat (JTextField für ein Textfeld, JLabel für Label etc).

Wenn diese zwei Dinge gemacht sind, können Sie auf das Steuerelement zugreifen nachdem SwingEngine.render aufgerufen wurde.

Der Konstruktor sieht in unserem Fall also folgendermassen aus:

    public SampleFormSwiXML2() {
        this.setupUI();
        this.fillComboBox();
        this.initFirstName();
        this.frame.pack();
    }

Die Methode initFirstName ist relativ einfach:

    private void initFirstName() {
        this.firstNameTextField.setText("default value, generated at "
                + (new Date()));
    }

Sobald das gemacht ist, kann das Programm kompiliert werden. Das Ergebnis wird etwa so aussehen, wie im Screenshot unten.

Übrigens - den Code dazu finden Sie in dieser Zip-Datei, unter SampleFormSwiXML2.

XML-GUI-Deskriptoren aus JAR-Dateien laden

Wenn Sie die Methode setupUI ansehen, werden Sie sich möglicherweise fragen, wieso wir hier einen Classloader verwenden.

    protected final void setupUI() {
        try {
            (new SwingEngine(this)).render(this.getClass().getClassLoader()
                    .getResource(UI_DEF_FILE_NAME));
        } catch (Exception exception) {
            System.err.println("An error occured when "
                    + "setting up the user interface.");
        }
    }

Natürlich könnte man den XML-GUI-Deskriptor wie jede andere Datei laden (ohne Classloader).

Dieser Code hat jedoch einen wichtigen Vorteil - er funktioniert auch dann, wenn die Anwendung und der XML-GUI-Deskritptor in einer JAR-Datei verpackt sind.

Classloader-Tricks

Sehen wir uns die setupUI-Methode nochmal an:

    protected final void setupUI() {
        try {
            (new SwingEngine(this)).render(this.getClass().getClassLoader()
                    .getResource(UI_DEF_FILE_NAME));
        } catch (Exception exception) {
            System.err.println("An error occured when "
                    + "setting up the user interface.");
        }
    }

In einigen seltenen Fällen wird diese Methode nicht funktionieren - der XML-GUI-Deskriptor wird für den Classloader nicht sichtbar sein.

Das kann passieren, wenn Sie versuchen, SwiXML in einer eclipse RCP-Anwendung zu verwenden.

In einer eclipse RCP-Anwendung gibt es allerdings mehrere Classloader - und einige davon sehen den XML-GUI-Deskriptor sehr wohl.

Um SwiXML auch innerhalb einer eclipse RCP-Anwendung verwenden zu können, muss man dem Konstruktor einen passenden Classloader übergeben.

Wir refakturieren den Code folgendermassen:

    public SampleFormSwiXML2(ClassLoader cl) {
        this.setupUI(cl);
        this.fillComboBox();
        this.initFirstName(cl);
        this.frame.pack();
    }
 
    protected void setupUI(ClassLoader cl) {
        try {
            (new SwingEngine(this)).render(cl.getResource(UI_DEF_FILE_NAME));
        } catch (Exception exception) {
            System.err
                    .println("An error occured when setting up the user interface.");
        }
    }

Jetzt können Sie die Dialogbox mit folgendem Code anzeigen:

    public SomeClass
    {
      public void showUi()
      {
        // UI_DEF_FILE_NAME is visible to the class loader of SomeClass
        SampleFormSwiXML2 ui = null;
 
        ui = new SampleFormSwiXML2(this.getClass().getClassLoader());
        ui.getFrame().setVisible(true);
      }
    }

Wenn der Classloader von SomeClass den XML-GUI-Deskriptor (UI_DEF_FILE_NAME) sieht, wird die Dialogbox korrekt angezeigt werden, auch wenn der Classloader von SampleFormSwiXML2 UI_DEF_FILE_NAME nicht sieht.

Event-Handling in SwiXML

Um SwiXML in richtigen Anwendungen einsetzen zu können, müssen wir in der Lage sein, auf Aktionen des Benutzers zu reagieren.

Zum Beispiel wollen wir, dass eine Message-Box erscheint, wenn der Benutzer die OK-Schaltfläche betätigt.

Sehen wir uns zunächst einmal den XML-GUI-Deskriptor an:

<button id="okButton" action="okButtonAction" text="OK">
        <gridbagconstraints insets="5,5,5,5" gridx="0" gridy="0" 
                anchor="GridBagConstraints.WEST"/> 
</button>

In der ersten Zeile steht das Tag action. Der Wert dieses Tags ist der Name der Instanzvariable, in der jene Aktion steht, die beim Drücken des Buttons ausgeführt werden soll.

Hier heisst die Aktion okButtonAction.

Daher muss unsere Klasse SampleFormSwiXML3 eine entsprechende Instanzvariable haben.

    /**
     * This is the action, which is invoked whenever the user
     * presses the OK button.
     *
     * We need to make it public, otherwise it doesn't work with SwiXML, sorry.
     */
    public Action okButtonAction = new AbstractAction() {
        /**
         * Default serial version ID.
         */
        private static final long serialVersionUID = 1L;
 
        public void actionPerformed(final ActionEvent event) {
            JOptionPane.showMessageDialog(null, "OK button pressed");
        }
    };

Jetzt wird bei jedem Drücken des OK-Buttons eine Message-Box angezeigt.

Weitere Schritte

Alle Beispiele (inkl. Quellcode), die in diesem Artikel präsentiert wurden, können Sie hier gratis downloaden.

Wenn Sie SwiXML in Ihren eigenen Anwendungen einsetzen wollen, empfehle ich Ihnen, einen Blick auf die wahrscheinlich grösste Sammlung von SwiXML-Deskriptoren im deutschsprachigen Raum zu werfen. Dort finden Sie über 30 kostenlose SwiXML-Deskriptoren, die Sie in Ihren Anwendungen verwenden können.

Last but not least können Sie für SwiXML-bezogene Fragen auch mit uns Kontakt aufnehmen.

Newsletter

Diese Website wird laufend aktualisiert und es kommen regelmässig neue Artikel hinzu.

Wenn Ihnen dieser Artikel gefallen hat, können wir Sie benachrichtigen (höchstens ein Mal im Monat) sobald neue Informationen veröffentlicht werden. Melden Sie sich dazu bei unserem Newsletter an.

 
module_des_erfolgs/swixml.txt · Zuletzt geändert: 28.06.2009 01:46 von dp
 
© Copyright Dmitri Pissarenko Softwareentwicklung