Grafische Benutzeroberflächen mit JavaFX Einführung

Grafische Benutzeroberflächen mit JavaFX
Einführung
Grafische Benutzeroberflächen
Objektorientierte Bibliotheken
I
Windows Presentation Foundation
(C#, andere .NET-Sprachen)
I
Qt (C++)
I
Cocoa (Objective C)
I
GTK (Objektimplementierung in C)
I
Swing, SWT, JavaFX (Java)
I
...
Im Praktikum behandelt: JavaFX
Stage, Scene, Node
Stage, Scene, Node
Stage
Node
Scene
JavaFX Grundgerüst
JavaFX-Programme erben von der abstrakten Klasse
Application.
Die abstrakte Methode
public void start ( Stage stage )
muss implementiert werden.
I
Beim Start des Programms wird ein Fenster (Stage) erzeugt
und der Methode start als Parameter übergeben.
I
Dort kann eine Szene hinzugefügt werden und das Fenster
dargestellt werden.
I
Die Anwendung wird automatisch beendet, wenn das letzte
Fenster geschlossen wird.
JavaFX Grundgerüst
import javafx . application . Application ;
import javafx . scene .*;
import javafx . stage . Stage ;
public class Main extends Application {
@Override
public void start ( Stage stage ) throws Exception {
Node node = new Label (" Hello World " );
// Group erbt von Node und erlaubt die Gruppierung
// mehrerer Nodes .
Group root = new Group ();
root . getChildren (). add ( node );
Scene scene = new Scene ( root , 300 , 100);
stage . setScene ( scene );
stage . show ();
}
}
Nodes
Die verschiedenen graphischen Elemente sind durch
Unterklassen von Node repräsentiert.
Beispiele:
I
Steuerelemente: Label, TextField, Button, Slider, . . .
I
Geometrische Formen: Line, Circle, Polygon, . . .
Darstellung eines Polygons:
Polygon polygon = new Polygon ();
polygon . getPoints (). addAll ( new Double []{
0.0 , 0.0 ,
20.0 , 10.0 ,
10.0 , 20.0 });
root . getChildren (). add ( polygon );
Nodes
Container-Nodes
Mehrere Node-Objekte können zu einem einzigen Node-Objekt
zusammengefasst werden.
Beispiele: Group, Region, Pane, BorderPane, HBox, VBox, . . .
Instanzen dieser Unterklassen von Node speichern eine Liste
von Kinder-Nodes.
Group root = new Group ();
root . getChildren (). add ( polygon );
// root . getChildren () gibt ist die Liste
// der Kinder - Nodes
Nodes
Container-Nodes
I
Group (Unterklasse von Node):
– Kind-Nodes erhalten ihre gewünschte Größe
– Gruppe ist gerade groß genug, um alle enthaltenen
Objekte zu umschließen
Group
Node 1
Node 2
Node 3
Nodes
Container-Nodes
I
Region (Unterklasse von Node):
– Größe unabhängig von Größe der Kinder
– jede Region definiert minimale, gewünschte und
maximale Größe
– bei Größenänderungen werden die Kinder neu
angeordnet und evtl. ihre Größe angepasst
– verschiedene Unterklassen implementieren verschiedene
Layouts
Nodes
Unterklassen von Region
I
Pane:
– manuelle Größe und manuelles Layout der Kinder
– Clipping möglich
Pane
Node 1
Node 2
Nodes
Unterklassen von Region
I
HBox:
– horizontale Anordnung der Kinder
– Verhalten der Kinder bei Skalierung kontollierbar
HBox
Node 1
Node 2
Node 3
Beispiele für Verhalten bei Vergrößerung:
I
die Kinder behalten ihre Größe und werden
gleichmäßig angeordnet, z.B. zentriert
I
die Kinder werden zu gleichen Teilen vergrößert, um
den zusätzlichen Platz zu füllen
I
nur Node 2 wird vergrößert, um den Platz zu füllen
Nodes
Unterklassen von Region
I
VBox: analog zu HBox, nur vertikal
VBox
Node 1
Node 2
Node 3
Nodes
Unterklassen von Region
I
BorderPane
BorderPane
Top
Left
Center
Right
Bottom
I
GridPane
GridPane
I
(0,0)
(1,0)
(2,0)
(0,1)
(1,1)
(2,1)
usw. (siehe Javadoc-Dokumentation)
Automatisches Layout
Das Layout der GUI-Elemente wird durch die geeignete
Schachtelung von Container-Nodes bestimmt.
BorderPane
Center: Pane
Bottom: HBox
Label
Slider
Label
JavaFX Koordinaten
Jedes Node-Objekt hat sein eigenes Koordinatensystem.
(0, 0)
In Container-Nodes beziehen sich Position
und Größe der Kinder immer auf das
Koordinatensystem der enthaltenden Node.
y
Koordinaten sind double-Werte.
I
Koordinaten einer Scene können per Default als Pixel
verstanden werden.
I
Nodes können mit Transformationen (z.B. Skalierung oder
Rotation) verändert werden.
– Dann sind die Koordinaten keine Pixel mehr.
– Daten müssen oft nicht per Hand in Pixel-Koordinaten
umgerechnet werden.
x
JavaFX Koordinaten
Auf jedes Node-Objekt können affine Transformationen
angewendet werden:
I
Translation (setTranslateX, setTranslateY)
I
Rotation (setRotate)
I
Skalierung (setScaleX, setScaleY)
JavaFX Koordinaten
Beispiel: manuelles Layout in Pane
Pane root = new Pane ();
Line l = new Line (0 , 0, 100 , 100);
Pane p = new Pane ();
p . translateX (50); // Verschiebung : addiere 50 zu x
p . translateY (50); // Verschiebung : addiere 50 zu y
Rectangle r =
new Rectangle (50 , 50 , 20 , 20);
p . getChildren (). addAll (r );
root . getChildren (). addAll (l , p );
root
l
p
r
JavaFX Koordinaten
Beispiel: Rotation von Nodes
Pane root = new Pane ();
Line l = new Line (0 , 0, 100 , 100);
Pane p = new Pane ();
p . translateX (50); // Verschiebung : addiere 50 zu x
p . translateY (50); // Verschiebung : addiere 50 zu y
p . rotate (30);
// Rotation um 30 Grad
Rectangle r =
new Rectangle (50 , 50 , 20 , 20);
p . getChildren (). addAll (r );
root . getChildren (). addAll (l , p );
root
l
p
r
JavaFX Koordinaten
Jedes Node-Objekt hat weiterhin eine Liste von
Transformationen, die nacheinander angewendet werden.
Damit können kompliziertere Transformationen leicht
zusammengesetzt und verändert werden.
// 1. Verschieben um (50 , 50)
node . getTransforms (). add ( new Translate (50 , 50));
// 2. Skalieren um Faktor 1.5 mit Mittelpunkt (10 , 10)
node . getTransforms (). add ( new Scale (1.5 , 1.5 , 10 , 10));
// 3. Rotation um 30 Grad mit Mittelpunkt (50 , 30)
node . getTransforms (). add ( new Rotate (30 , 50 , 30));
Vollständiges Beispiel
BorderPane
Center: Pane
Bottom: HBox
Label
Slider
Label
Styling mit CSS
Details des Aussehens können über Stylesheets gesteuert
werden.
Datei: style.css
.hbox {
-fx-background-color: lightblue;
-fx-alignment: center;
-fx-padding: 15;
-fx-spacing: 5;
}
#rect {
-fx-fill: linear-gradient(from 0% 0% to 100% 100%, red 0%, black 100%);
-fx-stroke: darkblue;
-fx-stroke-width: 5;
}
#line {
-fx-stroke: darkblue;
-fx-stroke-width: 5;
}
Styling mit CSS
Auswahl des Stylesheets im Java-Code:
// hbox soll Stil von CSS - Klasse . hbox lesen
hbox . getStyleClass (). add (" hbox " );
// line soll Stil von CSS - Id # line lesen
line . setId (" line " );
// rec soll Stil von CSS - Id # rect lesen
rectangle . setId (" rect " );
// CSS - Datei liegt im Projekt
borderPane . getStylesheets (). add (" beispielFX / res / style . css " );
Dokumentation des CSS-Formats: JavaFX CSS Reference
Interaktion I
Veränderliche Werte werden in JavaFX durch Property-Objekte
repräsentiert.
Beispiel: Klasse Slider
// Wert des Sliders
DoubleProperty valueProperty ()
// Hoehe der Node
DoubleProperty heightProperty ();
// Ist der Mauszeiger gerade ueber der Node ?
ReadOnlyBooleanProperty hoverProperty ();
usw .
Property-Objekte
Ziel: Reagiere auf jede Änderung eines Werts in der GUI,
z.B. die Position des Schiebereglers.
Idee: Anstelle eines double-Werts wird die Position des
Schiebereglers durch ein DoubleProperty-Objekt repräsentiert.
I
DoubleProperty kapselt einen privaten double-Wert.
I
Zugriff auf den gekapselten Wert mit Getter und Setter:
public void set ( double v );
public double get ();
I
Bei jeder Änderung des Werts (nur möglich über set),
werden alle Interessenten über die Änderung informiert.
I
Interessenten können sich mit der Methode addListener als
“Zuhörer” registrieren. Sie werden dann über alle
Änderungen informiert.
Property-Objekte
Beispiel: Hinzufügen eines Zuhörers für Änderungen an der
Schieberegerposition:
// Hinzufuegen eines Zuhoerers :
slider . valueProperty (). addListener ( new Zuhoerer ());
Der Zuhörer (Listener):
class Zuhoerer implements ChangeListener < Number >(){
@Override
public void changed ( ObservableValue <? extends Number > o ,
Number oldValue ,
Number newValue ) {
System . out . println (" Slider bewegt von "
+ oldValue + " nach "
+ newValue );
}
});
Property-Objekte
Oft implementiert man Listener-Klassen als anonyme Klassen.
Beispiel:
slider . valueProperty (). addListener (
new ChangeListener < Number >(){
@Override
public void changed ( ObservableValue <? extends Number > o ,
Number oldValue , Number newValue ) {
rectangle . rotateProperty (). set (
slider . valueProperty (). get ()
);
grad . setText ( newValue . intValue () + "\ u00B0 " );
}
});
I
es wird ein Objekt erzeugt, welches das Interface
ChangeListener implementiert
I
die Klasse des Objekts ist anonym
(Name ist nur intern auf JVM bekannt)