JavaFX

JavaFX2.0: Control

Unter Control werden all die GUI-Komponenten verstanden, die Benutzereingaben entgegennehmen. Dabei decken die Controls ein breites Spektrum von Funktionalität ab. Alle Controls lassen sich umfassend über CSS konfigurieren. Da gibt es zum Einen Controls, die dem Benutzer erlauben Eingaben bzw. Selektionen zu machen: ChoiceBox, ListView, Slider, TableView, TextInputControl und TreeView.

Andere Controls dienen der reinen Anordnung und Darstellung von anderen Controls (nicht zu verwechseln mit Layout-Klassen!): Accordion, MenuBar, Separater, ScrollBar und ScrollPane, SplitPane, TabPane und ToolBar.

Dann gibt es noch ein paar spezielle Controls, wie z.B. die Super-Klasse Labeled, die für Cells, Buttons, Labels und den Titel bei TitledPane zuständig ist. In diese Kategorie fällt auch der ProgressIndicator.

Die erste Gruppe rund um TreeView und ChoiceBox, umfasst altbekannte Komponenten, wie sie jedes GUI-Framework kennt. Wer aus dem Swing-Umfeld kennt, kann sich an die vielen dedizierten Klassen, für alle möglichen Details erinnern. Dies wurde von Vielen als Over-Design gewertet und in JavaFX entrümpelt. Als Basis für alle JavaFX-Controls, die eine Sammlung von Optionen enthalten, dient eine ObservableList als Container. Sobald sich die, mit dem Control verknüpfte ObservableList ändert, wie dessen Inhalt entsprechend aktualisiert. Wichtig ist hier, dass man die ursprünglich hinterlegte ObservableList verwendet und nicht permanent neue Listen anlegt und zuweist.

Beim zweiten Block gibt es kaum Überraschungen. Einzig das Accordion ist vielen neu. Dabei handelt es sich um eine Gruppe von TitlePanes, die untereinander dargestellt werden und von denen der Benutzer immer nur eins aufgeklappt haben kann.

Bei der letzten Gruppe ist vor allem die Klasse Cell (Subklasse von Labeled) interessant. Diese Klasse ist - im Vergleich zu Swing - konzeptionell neu, denn sie stellt eine einheitliche Komponente für ein Element in einer ListView, einer TableView oder einer TreeView dar. Da eine Cell normalerweise Text enthält, gibt es dafür ein entsprechendes Attribut. Wenn man aber eine grafische Darstellung innerhalb einer Cell benötigt, lässt sich ein beliebiger Node anhängen.

JavaFX2.0: Parent, Group und Region

Diesmal möchte ich die Subklassen von Parent anschauen, die für das Management von Darstellung und Layout verwendet werden. Zuerst ein kurzer Ausflug hinter die Kulissen: JavaFX2.0 arbeitet mit einem sogenannten Pulse. Dabei handelt es sich um einen Takt des Grafik-Subsystem, der 60 Mal in der Sekunde prüft, ob sich am scene graph etwas geändert hat, ob Bereiche des GUIs neu gezeichnet werden müssen und so weiter. Änderungen an Instanzen, die im Scene Graph abgelegt sind, werden also umgehend umgesetzt. Auf den Pulse hat man keinen Einfluss. Mehr oder weniger Durchläufe pro Sekunde sind also nicht möglich. (Stichwort Games, die gerne mit FPS werben: 60 Vollbilder pro Sekunde sind aber absolut ausreichend, denn das menschliche Auge gibt schon viel früher auf).

Von Parent sind vier Klassen abgeleitet: Control, Group, Region und WebView. Control umfasst sämtliche grafischen GUI-Elemente, während WebView das macht, was der Name suggeriert. Zum diesen beiden in separatem Blog mehr...

Mich interessieren jetzt erst mal Group und Region. Eine Group enthält eine ObservableList mit GUI-Elemente, die der Reihe nach gezeichnet werden, wenn die Group gezeichnet wird. Dabei wird die Grösse einer Group durch die enthaltenen GUI-Elemente bestimmt. Dabei wird jeweils die preferred size herangezogen. Die automatische Grössenanpassung unterbindet Group. Die Group ist eher für rein grafische Dinge geeignet und weniger für Controls. Dies vor allem wegen dem fehlenden CSS Styling Support.

Eine Region kann durch CSS gestyled werden. Details zum CSS3-Background Support finden sich hier. Eine Region ändert nichts am Layout der enthaltenen Komponenten. Dieses wird von den Subklassen von Region übernommen: StackPane, HBox, VBox, TilePane, FlowPane, BorderPane, GridPane, or AnchorPane.

JavaFX2.0: Stage und Scene

Die Basis aller JavaFX 2.0 GUIs (egal ob im Web oder auf dem Desktop) ist Stage. Diese Klasse ist von Window abgeleitet, wie auch die Klasse PopupWindow.Das System stellt in der launch()-Methode eine Stage-Instanz zur Verfügung. Wie die Stage aussieht, kann man auf verschiedene Arten definieren: bei Fenstern können verschiedene Dekorationen ( enum StageStyle) definiert werden. Ausserdem kann ein Fenster als screen-füllend oder skalierbar definiert werden (resizable(true/false)). Bei jedem Stage kann festgelegt werden, ob es sich um ein modales Fenster handelt(initModality()) und zu welchem anderen Fenster es gehört. Dieses Fenster würde dann durch ein modales Fenster blockiert. Es gibt drei verschiedene Arten von Modality: NONE, APPLICATION_MODAL und WINDOW_MODAL. Während sich APPLICATION_MODAL auf die gesamte Applikation bezieht, blockiert WINDOW_MODAL lediglich die Hierarchie der Fenster, wie durch initOwner(...) definiert. Auf dieser Stage (engl. Bühne) wird in einer Scene (engl. Szene) der Screen-Inhalt aufgebaut.

Im Gegensatz zu Swing basiert JavaFX2.0 auf einem sogenannten Scene Graph, ähnlich HTML. Dabei handelt es sich um eine Baumstruktur, die den Inhalt eines Fensters beschreibt und in einer Scene abgelegt wird. Diese Scene wird so einem Fenster zugewiesen: myStage.setScene(myScene);

Für die Konstruktion einer Scene ist mindestens ein Parent notwendig. Ein Parent ist ein spezieller Typ von Node und Node ist die Basisklasse sämtliche Klassen die einen Block im GUI repräsentieren (egal, ob unsichtbare oder sichtbare Komponenten; dazu später mehr).

Auf dem Level einer Scene lassen sich unter Anderem Tastaturkürzel (Accelerators) definieren. Eine ähnliche Variante ist der Einsatz von Mnemonics. Der Unterschied zum Accelerator liegt darin, dass hier ein Tastatürkürzel mit einem GUI-Komponente (Node) verknüpft wird. Sobald ein Benutzer eine Tasten-Kombination drückt, wird ein ActionEvent an die definierte GUI-Komponente ausgelöst. Desweiteren lässt sich der Typ des Cursors setzen. Zur Auswahl etwa 20 Varianten, die in der enum Cursorgespeichert sind.

Eine Scene kann eine breite Palette von Events behandeln (diverse Eingabe-Variationen mit Maus und Tastatur, und Drag'n'Drop).

Auf diesem Level lassen sich CSS als Stylesheets definieren. Da JavaFX sehr umfassenden CSS-Support anbietet, empfiehlt es sich möglichst viel des Erscheinungsbilds in CSS auszulagern. Zu beachten ist, dass JavaFX-basiertes CSS nicht 100% mit Web-basiertem CSS übereinstimmt. Die Verwendung ist sehr ähnlich, aber es gibt durchaus Unterschiede. Von der Verwendung von JavaScript rate ich eher ab. Schliesslich können wir GUI-nah in Java programmieren; da macht ein Einsatz von JavaScript eher wenig Sinn.

Im nächsten Blog gehe ich auf die Subklassen von Node ein, die für das Layout von GUIs verwendet werden.

automatisches Scaling von JavaFX2.0-Controls

Getestet habe ich mit folgendem Code-Block unter JavaFX2.1-Beta unter Mac OS X 10.7 und Java 6:

 HBox p = HBoxBuilder.create().children(btn, btn1).build();
 HBox.setHgrow(btn, Priority.NEVER);
 HBox.setHgrow(btn1, Priority.ALWAYS);

Folgende Liste enthält die JavaFX2-Controls, die ihre Grösse in einer HBox (oder anderem Layout, welches die enthaltenen Elemente skaliert) automatisch anpasst:

- javafx.scene.control.ListView - javafx.scene.control.Slider - javafx.scene.control.TableView - javafx.scene.control.TextField - javafx.scene.control.TreeView

Diese JavaFX2-Controls benötigen das explizite Setzen der maximalen Breite bzw. Höhe:

- javafx.scene.control.Button - javafx.scene.control.ProgressBar - javafx.scene.control.ToggleButton

Bei folgenden Gui-Controls macht Scanning keinen Sinn:

- javafx.scene.control.CheckBox - javafx.scene.control.Label - javafx.scene.text.Text

javafx.scene.control.TextArea ignoriert Hgrow komplett

Tipps und Tricks zu JavaFX2.0

Ich lerne gerade JavaFX2.0 kennen und sammle hier meine Erkenntnisse zu einzelnen Themen. Wieso JavaFX2.0? Hauptgrund sind zum Einen die Erwartungen der Benutzer, die mit einem simplen Formular und ein paar Bildchen nicht mehr zu begeistern sind und zum Anderen die unüberschaubare Anzahl von Web-Frameworks, die am Ende des Tages doch an Browser-Inkompatibilitäten scheitern.

Bei JavaFX2.0 geht es darum, die Produktivität zu erhöhen, in dem der Benutzer seine gewohnten Handgriffe schneller und zuverlässiger ausführen kann.

Dabei geht's um verschiedene Aspekte: Funktionalität, Zuverlässigkeit und Optik.

Los geht's mit Funktionalität:

JavaFX2.0 ist Java. Alles was in der Vergangenheit clientseitig funktioniert hat, ist immer noch korrekt. Dazu kommen starke Controls, wie WebView, TableView, TreeView und Toolbar und GUI-Elemente für die Organisation des GUIs, wie Accordion. Dazu gleich mehr ...

Zentral sind Properties und Bindings. Properties erinnern an getter-/setter-Methoden aus JavaBeans-Zeiten (also: gestern). Zusätzlich zur Basisfunktionalität der Kapselung bieten Properties Observable-Funktionalität und Support für Binding. Grundsätzlich können Properties über Binding verknüpft werden. Das bedeutet, dass bei der Änderung eines Properties andere Properties automatisch neu gesetzt (z.B. berechnet) werden. Mehr Infos hier. Beide Themen sind zentral für JavaFX.

Tipp zu Binding: für Layout nie Binding verwenden. Die Performance leidet und der Code wird unlesbar. Verwende die entsprechenden Panes für die optische Anordnung von GUI-Elementen. Optimal für Bindings sind die Datenstrukturen hinter Tabellen, Trees oder eigenen JavaFX2.0 GUI-Elementen. Der Callback in die GUI Aktualisierung entfällt, wenn Datenstrukturen direkt mit den zugehörigen Elementen verknüpft sind.

JavaFX2.0 kommt mit neuen Collections (List und Map-style), die einen über inhaltliche Änderungen informieren. Beide lassen sich von FXCollections aus herkömmlichen Collections erzeugen.

WebView

Hierbei handelt es sich um eine komplette webkit-basierte HTML-Engine inkl. JavaScript, die überall ins GUI integriert werden kann. Der Inhalt kann über eine URL oder direkt als HTML übergeben werden.

URL:

 WebView wv = new WebView();
 WebEngine we = wc.getEngine();
 we.load("http://...");

direkt als HTML:

 WebView wv = new WebView();
 WebEngine we = wc.getEngine();
 we.loadContent("...");

TableView

Im Unterschied zu Swing entfällt die Implementierung eines TableModels und die ganze optische Gestaltung ist in CSS ausgelagert. Somit beschränkt sich die Implementierung der TableView auf die Funktionalität.

Dank Generics können Tabellen einfacher gebaut werden als früher unter Swing. Dazu braucht man zuerst ein Model-Klasse, die eine Zeile der Tabelle repräsentiert. Dazu reicht im Prinzip ein POJO; in den Beispielen sieht man häufig Beans mit Properties. Dies hilft natürlich sehr, wenn man automagisch aktualisierte Werte braucht.

Wenn man das Model hat, definiert man Tabellenspalten und fügt sie der TableView hinzu. Im Unterschied zu früher ist Sortierung (auch nach mehreren Spalten [aktivierbar mit SHIFT] ) standardmässig integriert. Die Optik kann mit CSS angepasst werden.

TreeView

Ähnlich wie bei der TableView entfällt auch hier die explizite Model-Implementierung. Man baut einen Baum mit TreeItems mit der Methode node.getChildren().add(myNode). Als Label für ein TreeItem wird toString() auf dem enthaltenen Value verwendet.

ToolBar

Eine ToolBar bietet kaum Überraschungen. Es lassen sich Effekte auf eine ToolBar anwenden; sinnvoll ist dies nicht. Schön wäre es eine ToolBar detachable zu definieren. Dies scheint aber (noch) nicht zu gehen.

 

Nun zur Zuverlässigkeit:

Es lohnt sich Auswahllisten und Daten, die selten ändern oder gar historische Daten sind, lokal zu speichern (mindestens in memory, evtl. auch echt persistent). Sämtliche aus der Client-Server-Entwicklung bekannten Tricks sind hier nicht verkehrt. Einzig das Bauen von GUIs "auf Vorrat" lohnt sich weniger. Gegenüber Panel-basierten GUIs mit GridBagLayout ist die Performance-Steigerung bei JavaFX2.0 enorm.

Zum Schluss die Optik:

Keep it simple! Perfekte Farbkombinationen und Schatten werden nie entsprechend honoriert. Im Gegenteil! Die hier verwendete Zeit bekommt keine Budget-Position. Beim Bauen eines GUIs mit einfachen Layouts anfangen und Benutzer-Feedback abwarten. Mit JavaFX2.0 sind GUIs so schnell neu oder umgebaut und diese Arbeiten sind so non-invasiv, dass ein gewaltiges Investment in Optisches fehlgeleitet ist.

Layout

HBox und VBox sollten nicht sehr tief geschachtelt werden. Das kostet Performance. Wahrscheinlich hilft eine GridPane diese Verschachtelungen zu vermeiden. Verwende kein Binding damit ein GridPane sich der Grösse des Fensters anpasst.

Gradients

Farbübergänge lassen sich in JavaFX2 sehr einfach erzeugen. Es gibt zwei Klassen dafür: LinearGradient und RadialGradient mit dem jeweiligen Buildern dazu. Am Besten zu Enterprise GUIs passen üblicherweise dezente, lineare Farbübergänge im Graubereich (oben hell / unten etwas dunkler) auf Fensterebene. Mit einem dunkleren Farbübergang (oben dunkelgrau / unten schwarz) lässt sich ein Adobe Flash Effekt erzielen. Dies kann hilfreich sein, wenn man User dieser Plattform "abholen" will.

CSS

Einem GUI (egal ob browser-basiert oder window-basiert) liegt immer ein DOM-Baum zugrunde, der die Struktur dieses GUIs definiert. Will man nun einen Knotenpunkt (Node) innerhalb eines solchen DOMs finden, kann man entweder drüber iterieren (beginnend mit myScene.getRoot()) oder man sucht den Node direkt über seinen Namen:

 Node n = parentNode.lookup("...");

Dies setzt natürlich voraus, dass vorgängig mit myNode.setId("...") eine eindeutige Id vergeben wurde.

Diese Ids können dann in CSS-Files verwendet werden.

Hier ein paar Tricks, wie man einer Applikation einen einheitlichen, neuen Farbstil geben kann:

-fx-base:/* Basisfarbe für alle Komponenten */

-fx-text-base-color:/* Textfarbe */

-fx-accent: /* Standard Knöpfe ... */

-fx-focus-color: /*Farbe für die Anzeige des aktuellen Fokus */

-fx-mark: /* Farbe für Markierungen (CheckBoxen, Menüs, ...) */

Animationen

JavaFX2.0 ermöglicht es fast Alles irgendwie zu animieren. Aber man muss hier natürlich den Anwendungsfall im Auge behalten. Der Einsatz von Animationen in Spielen drängt sich ja meistens geradezu auf, aber in Business Applikationen ist davon abzuraten. Es mag die ersten paar Mal toll aussehen, aber schon nach kurzer Zeit nervt's den Benutzer. Es mag Fälle geben, in dem man den Benutzer mit einer Animation bspw. auf einen aktuell laufenden Prozess hinweisen möchte, aber im Zweifelsfall: Finger weg von Animationen!

Effekte

JavaFX bietet eine Vielzahl von Effekten. Hier gilt aber dasselbe, wie bei Animationen: nur mit besonders gutem Grund brauchen. Vielleicht eine Ausnahme sind Fade-Out und Fade-In. Sie können gut funktionieren, wenn sie dem Benutzer einen klaren Hinweis geben sollen (z.B."Ich brauche hier Input"). Aber dann sollten sie schnell sein (ca 250 ms) und konsistent verwendet werden.

Bei allen Aspekten rund um die Optik: eine kleine Bibliothek von vordefinierten Styles anlegen und diese dann entsprechend und konsequent verwenden. Das gibt ein einheitliches GUI. Unbedingt CSS einsetzen, damit die Optik später von einem CSS-Profi anpassbar ist. Niemand will im Java-Code die Optik pflegen.