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)
© Copyright 2024 ExpyDoc