Deklarative und programmierte Oberflächen: Swing und JavaFX im Vergleich

Grundsätzlich können grafische Oberflächen über eine Programm-API aufgebaut werden oder in einer deklarativen Beschreibung spezifiziert werden.

· Programmierte Oberflächen: Der traditionelle Bau von grafischen Oberflächen in Java weist die Besonderheit auf, dass das Design der Oberfläche in Java-Code gegossen werden muss. Jede Komponente muss mit new erzeugt werden und mithilfe eines Layouts explizit angeordnet werden. Komplexe Oberflächen bestehen dann aus fast unwartbaren Mengen von Programmcode zum Aufbau der Komponenten, zum Setzen der Eigenschaften und Platzierung auf dem Container. Die Änderung des Layouts ist natürlich sehr schwierig, da mitunter auch für kleinste Änderungen viel Quellcode bewegt wird. In der Versionsverwaltung sieht das mitunter schrecklich aus.

· Deklarative Oberflächen stehen im Gegensatz zu den programmierten Oberflächen. Bei ihnen ist die Beschreibung des Layouts und die Anordnung der Komponenten nicht in Java ausprogrammiert, sondern in einer externen Ressourcen-Datei beschrieben. Das Format kann etwa XML sein, und spiegelt wieder, wie das Objektgeflecht aussieht. Eine Ablaufumgebung liest die Ressourcen-Datei und übersetzt die Deklarationen in ein Geflecht von GUI-Komponenten. Im Hauptspeicher steht dann am Ende das gleiche wie bei der programmierten GUI: ein Objekt-Graph.

Das andere Ufer: Microsoft erkannte ebenfalls die Notwendigkeit einer deklarativen Beschreibung von Oberflächen und nutzt intensiv XAML (Extensible Application Markup Language). Gleichzeitig gibt es leistungsfähige Tools und Designer für XAML. Die Firma Soyatec versucht sich mit eFace an einer Java-Realisierung (http://www.soyatec.com/eface/).

Gui-Beschreibungen in JavaFX

JavaFX unterstützt beide Möglichkeiten zum Aufbau von grafischen Oberflächen. Zum einen ist da die klassische API, die Knoten in einen Baum hängt, viel interessanter ist aber der deklarative Ansatz, der sehr schön Präsentation und Logik trennt. JavaFX selbst bietet eine Beschreibung auf XML-Basis, genannt FXML. XML ist selbst hierarchisch, kann also die grundlegende hierarchische Gliederung einer GUI in Containern und Komponenten sehr gut abbilden.

Neben FXML gibt es weitere proprietäre Beschreibungen und Mischformen. Eine davon ist FXGraph vom Projekt e(fx)clipse (http://www.eclipse.org/efxclipse/), einer JavaFX-Unterstützung in Eclipse. Die Beschreibung ist eine DSL[1] und definiert den Objekt-Graphen, der im Hintergrund in FXML umgesetzt wird. FXGraph ist kompakter als FXML und erinnert entfernt an JSON. Auch kann die JavaFX-API in alternativen Sprachen angesprochen werden, JavaScript und weitere Skriptsprachen wie Groovy (und der Hilfe von GoovyFX[2]) oder Scala (zusammen mit ScalaFX[3]) zeigen interessante Wege auf. Allerdings mischt sich dann doch wieder schnell die Deklaration der GUI mit Logik, was die Trennung zwischen Präsentation und Logik aufweicht. Es ist guter Stil, die Beschreibung der Benutzerschnittstelle und der Logik zu trennen, um auch Tests leichter zu realisieren.

Deklarative GUI-Beschreibungen für Swing?

Für AWT und Swing hat sich für deklarative Oberflächen in den letzten Jahren kein Standard gebildet, und Oberflächen werden heute noch so programmiert wie vor 15 Jahren. Dass Swing-Oberflächen immer programmiert werden müssen hält auf, auch wenn ein GUI-Builder heutzutage die Schmerzen minimiert. Über die WYSIWYG-Oberfläche wird in der Regel das Layout mit allen Komponenten zusammengeklickt und im Hintergrund erzeugt der GUI-Builder den Programmcode. Für die Laufzeitumgebung hat sich also nichts verändert, aber für uns schon.

Um auch in Swing in die Richtung von deklarativen Oberflächen zu kommen, gibt es unterschiedliche Open-Source-Lösungen, da Oracle nichts im Angebot hat.

· Swixml (http://www.swixml.org/) nutzt das XML-Format zur Beschreibung von GUIs und bildet jede Swing-Klassen auf ein XML-Element ab. Später nutzt Swixml dann SAX und JDOM, um die XML-Datei einzulesen und zu repräsentieren und um zur Laufzeit eine Swing-Komponentenbaum aufzubauen. Die Folien unter http://www.swixml.org/slides.html geben einen Einblick in die Möglichkeiten. Seit Mitte 2011 wird Swixml nicht mehr erweitert.

· Eine weitere Lösung zur deklarativen Beschreibung von Swing-Oberflächen bietet der Swing JavaBuilder (http://code.google.com/p/javabuilders/). Die Open-Source-Bibliothek steht unter der Apache-Lizenz und nutzt statt XML das kompaktere YAML-Format, dessen Schreibweise noch weiter verkürzt wurde. Das letzte Release stammt von Ende 2011, eine Weiterentwicklung ist unwahrscheinlich.

Die Inaktivität lässt entweder so erklären dass die Produkte perfekt sind, oder sich Entwickler mit den klassischen Code-generierenden GUI-Buildern anfreunden konnten, oder die Tools mit dem Aufkommen von JavaFX einfach unattraktiv werden.


[1] Eine domain-specific-language (DSL) ist eine „Spezialsprache“, die ein exklusives Format für einen klar abgegrenzten Anwendungsfall definiert.

[2] http://groovyfx.org/

[3] https://code.google.com/p/scalafx/

Neue JavaFX-Version (build 42), mal wieder API-Änderungen

Download unter http://javafx.com/. Änderungen betreffen wohl nahezu jedes Hauptprogramm, denn setVisible(true) wird zu show() und setVisible(false) zu hide(). Das ist natürlich super ungewöhnlich, da AWT genau den anderen Weg gegangen ist, und eben Bean-Methoden einführte. 2.: Die Builder sind in ein andere Paket gerutscht, so fällt weg: import javafx.builders.SplitPaneBuilder;
Achtung: Die Online-Doku ist im Moment noch nicht auf dem neusten Stand.

Deklarative JavaFX-Oberflächen mit FXML

Den Szene-Graph über Java Programmcode aufzubauen ist eine Möglichkeit, doch JavaFX erlaubt es auch, die Objekte über XML zu konfigurieren. Das erlaubt es viel einfacher, grafische Oberflächen über Gui-Builder aufzubauen und sauber das Layout (was) vom Code (wie) zu trennen.

Die hierarchische Struktur von XML passt natürlich prima zu der Hierarchie, die es bei grafischen Oberflächen gibt: Ein Fenster enthält Container, die wiederum Elemente enthalten, usw. Für unser kleines Beispiel soll eine Oberfläche drei Elemente bieten: Eine Beschriftung, ein Textfeld und eine Schaltfläche. Drückt der Anwender die Schaltfläche, soll der Text im Textfeld in Großbuchstaben konvertiert werden.

Die XML-Datei covert2UpperCase.fxml sieht so aus:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>

<HBox xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.tutego.insel.javafx.ButtonController">
  <children>
    <Label text="Eingabe: " />
    <TextField fx:id="input" />
    <Button text="Konvertiere" onAction="#convertAction" />
  </children>
</HBox>

Die Hierarchie ist gut zu erkennen. Die interessanten Dinge sind andere:

  1. Zu Beginn gibt es eine Art import-Anweisung um Typnamen nicht voll qualifizieren zu müssen. Für den Rückgriff auf Grafiken muss <?import javafx.scene.image.*?> eingebunden werden und <?import javafx.scene.*?> für Group, Node oder ähnliches, was wir im Programm aber alles nicht nutzen.
  2. HBox hat zwei Attribute: Ein Attribut deklariert den Namensraum fx, ein anderes eine Klasse, den sogenannten Controller, der später die Ereignisbehandlung für den Klick übernimmt. Die Typen in FXML heißen genauso wie die Klassennamen. Natürlich könne auch eigene Klassen eingebaut werden, sofern sie mit <?import> bekannt gemacht wurden.
  3. Das TextField bekommt mit dem Attribut fx:id eine ID zugewiesen, unter der das Textfeld später erfragt werden kann. JavaFX geht noch einen Schritt weiter, und bildet das Objekt mit der ID automatisch auf ein Attribut der Controller-Klasse ab. Label und Schaltfläche brauchen keine IDs, da sie nicht erfragt werden müssen.
  4. Das Attribut onAction der Schaltfläche referenziert Programmcode, der immer aufgerufen wird, wenn die Schaltfläche gedrückt wird. Hier kann direkt Java-Quellcode stehen, oder, wie in unserem Fall, ein # und der Name einer Methode, die in einem Controller deklariert werden muss. Den Klassenamen vom Controller haben wir am Wurzelelement deklariert.

Die Ereignisbehandlung ist komplett aus der FXML-Datei rausgezogen und wandert in Controller-Klassen. Die eigene Klasse ButtonController, die voll qualifiziert bei fx:controller genannt wurde, enthält:

com/tutego/insel/javafx/ButtonController.java

package com.tutego.insel.javafx;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class ButtonController
{
  @FXML
  private TextField input;

  @FXML
  protected void convertAction( ActionEvent event )
  {
    input.setText( input.getText().trim().toUpperCase() );
  }
}

Drei Dinge fallen ins Auge:

  1. Die Controller-Klasse erweitert keine Schnittstelle.
  2. Die Annotation @FXML sagt, dass der Verweis auf das TextField-Objekt aus dem Szene-Graphen in die Variable input injiziert werden soll.
  3. Da in der FXML-Datei an der Schaltfläche ein onAction="#convertAction" steht, muss das zu einer Methode zugeordnet werden. Die Annotation @FXML an der Methode unter dem Namen convertAction stellt diese Beziehung her.

Das Hauptprogramm ist nun relativ einfach:

com/tutego/insel/javafx/FXMLDemo.java, start()

@Override
public void start( Stage stage ) throws Exception
{
  Parent p = FXMLLoader.load( getClass().getResource( "covert2UpperCase.fxml" ) );
  stage.setScene( new Scene( new Group( p ), 500, 400 ) );
  stage.setVisible( true );
}

Was noch alles mit FXML möglich ist, beschreibt ein Dokument von Oracle: http://fxexperience.com/wp-content/uploads/2011/08/Introducing-FXML.pdf.

Neuer JavaFX b40 Build

Änderungen: Kein Zip mehr zum Auspacken, sondern eine exe. Keine Demos mehr dabei. APIs ändern sich, viele Beispiele muss ich anpassen. Bei einer Beta ist das natürlich OK, dennoch überlege ich mir, ob ich JavaFX unter diesen Umständen überhaupt in die Insel setzen kann. An großen FX-Apps kann man so nicht denken, die Änderungen machen einen verrückt.