1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

JavaFX Teil 1 2018-02-02

Kurze Einführung in JavaFx

  1. melmager
    JavaFX.8

    Bei Java 8 ist bei der Grafischen Oberfläche javaFX die elegantere Version gegenüber Swing.

    Steigt mit mir zusammen um :)

    Wenn man über GUI (Grafic user Interface) spricht stolpert man schnell über zwei verschiedenen Dreiecke die die theoretischen,
    Idee hinter einer GUI erklären.
    Zuerst das Dreieick Model, View, Controller (MVC) und dann das WMF von Microsoft das eine Trennung von Layout, Styleheet und Quellcode
    vorsieht. FX erfüllt beide.
    https://www.tutorials.de/attachments/fx8-mvc-png.65404/
    fangen wir mal mit dem Offensichtlichen an - dem View - sprich was angezeigt wird.

    Bei JavaFX führe da zwei Wege zum Ziel - und in diversen Büchern wird nur die Variante mit Javacode gezeigt.

    Die intressantere Variante ist allerdings die FXML Datei, auf die werde ich allein eingehen.
    Das ist eine XML Datei die einmal die Grafikelemente festlegt die anzeigt (View) werden und auch das
    Layout (wo wird das Element plaziert).

    es gibt auch einen Grafischen Editor den man in sein Lieblings IDE installieren kann.

    http://www.oracle.com/technetwork/java/javase/downloads/javafxscenebuilder-info-2157684.html

    Das View kann durch CSS Datei im Aussehen (Farbe) angepasst werden. Da das CSS über eine extra Datei geregelt wird, haben wir hier
    eine Trennung zwischen Layout und Style. Erklärung zum CSS kommt hier am Ende - Funktion geht vor Schönheit.

    Ich werde hier mal den Programmierer Klassiker "Hello World" etwas aufmotzen weil die Anzeige eines Satzes etwas langweilig ist.

    Ein Fenster mit Dropdown Menu für verschiedene Begrüssunsarten und ein Eingabefeld dem der
    Name des zu grüssenden mitgegeben werden kann. Ein Button zum speichern und dann kommt
    noch eine Tabelle wo die Begrüssungen aufgelistet werden, und zwar maximal acht.



    Die FXMLDocument.fxml Datei:

    Code (Text):
    1. <?xml version="1.0" encoding="UTF-8"?>
    2.  
    3.  
    4. <?import javafx.collections.*?>
    5.  
    6. <?import java.lang.*?>
    7.  
    8. <?import java.util.*?>
    9.  
    10. <?import javafx.scene.*?>
    11.  
    12. <?import javafx.scene.control.*?>
    13.  
    14. <?import javafx.scene.layout.*?>
    15.  
    16. <?import javafx.collections.*  ?>
    17.  
    18.  
    19. <AnchorPane id="AnchorPane" prefHeight="380.0" prefWidth="453.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapphello.FXMLDocumentController">
    20.  
    21.     <children>
    22.  
    23.       <Button fx:id="button" layoutX="180.0" layoutY="95.0" onAction="#handleButtonAction" text="Click Me!" />
    24.  
    25.       <TextField fx:id="name" layoutX="251.0" layoutY="38.0" text="Welt" />
    26.  
    27.       <ComboBox fx:id="anrede" layoutX="58.0" layoutY="38.0" prefWidth="150.0">
    28.      
    29.        <items>
    30.          
    31.         <FXCollections fx:factory="observableArrayList">
    32.              
    33.          <String fx:value="Hallo" />
    34.              
    35.          <String fx:value="Moin" />
    36.              
    37.          <String fx:value="Tag" />
    38.              
    39.          <String fx:value="HuHu" />
    40.          
    41.         </FXCollections>
    42.  
    43.        </items>
    44.  
    45.        <value>
    46.          
    47.         <String fx:value="Hallo" />
    48.      
    49.        </value>
    50.  
    51.        </ComboBox>
    52.  
    53.         <TableView fx:id="table" layoutX="58.0" layoutY="149.0" prefHeight="200.0" prefWidth="350.0">
    54.      
    55.          <columns>
    56.            
    57.           <TableColumn fx:id="xanrede" prefWidth="175.0" text="Anrede" />
    58.          
    59.           <TableColumn fx:id="xname" prefWidth="175.0" text="Text" />
    60.      
    61.          </columns>
    62.  
    63.         </TableView>
    64.  
    65.   </children>
    66.  
    67. </AnchorPane>
    Zuerst fangen wir mit dem Fenster an - Definiert durch "Pane". Im Beipiel ein AnchorPane. Die Grössenangaben lassen wir mal aussen vor,
    wichtig ist die Declaration "xmlns" - hier werden die nötigen xml namensräume festgelegt. Mit "fx:controller" gibt es ein Verweis
    auf den zurgehörigen Java Controller.
    Den Pfad kann man sich aus der Package Angabe der Java Classe ziehen.

    Was eine java Datei zu einem Controller macht sind die @FXML Annotation. Nur durch die vorangestelle @FXML Annoation wird die Variable
    oder Funktion überhaupt gesehen.

    Datei FXMLDocumentController.java

    Code (Text):
    1. package javafxapphello;
    2.  
    3. import java.net.URL;
    4. import java.util.ResourceBundle;
    5. import javafx.collections.FXCollections;
    6. import javafx.collections.ObservableList;
    7. import javafx.event.ActionEvent;
    8. import javafx.fxml.FXML;
    9. import javafx.fxml.Initializable;
    10. import javafx.scene.AccessibleAttribute;
    11. import javafx.scene.control.Button;
    12. import javafx.scene.control.ComboBox;
    13. import javafx.scene.control.Label;
    14. import javafx.scene.control.TableColumn;
    15. import javafx.scene.control.TableView;
    16. import javafx.scene.control.TextField;
    17.  
    18. /**
    19.  *
    20.  * @author susi
    21.  */
    22. public class FXMLDocumentController implements Initializable {
    23.  
    24.     private Label label;
    25.     @FXML
    26.     private Button button;
    27.     @FXML
    28.     private ComboBox<?> anrede;
    29.     @FXML
    30.     private TextField name;
    31.     @FXML
    32.     private TableView<XHello> table;
    33.     @FXML
    34.     private TableColumn<XHello, String> xanrede;
    35.     @FXML
    36.     private TableColumn<XHello, String> xname;
    37.  
    38.     private ObservableList<XHello> data = FXCollections.observableArrayList();
    39.  
    40.     @FXML
    41.     private void handleButtonAction(ActionEvent event) {
    42.         String inanrede = anrede.getSelectionModel().getSelectedItem().toString();
    43.         String inname = name.getText();
    44.         System.out.println("Click " + inanrede + " " + inname);
    45.         data.add(new XHello(inanrede, inname));
    46.         if (data.size() > 5) {
    47.             data.remove(0);
    48.         }
    49.  
    50.         if (!data.isEmpty()) {
    51.             System.out.println(data.toString());
    52.         }
    53.  
    54.     }
    55.  
    56.     @FXML
    57.     public void initialize() {
    58.  
    59.         xanrede.setCellValueFactory(cellData -> (cellData.getValue().anredeProperty()));
    60.         xname.setCellValueFactory(cellData -> (cellData.getValue().nameProperty()));
    61.  
    62.         table.setItems(data);
    63.         System.out.println("init fxml");
    64.     }
    65.  
    66.    @Override
    67.    public void initialize(URL url, ResourceBundle rb) {
    68.         // TODO
    69.         System.out.println("init override");
    70.         initialize();
    71.     }
    72.  
    73. }
    Die Verküpfung zwischen View und Controller geht über den Namen.
    In dem FXML ist dafür das Attribute fx:id="button" zuständlig, In der Controllerclasse
    @FXML
    Button button;
    setzt die zugehörige Variable.
    Wenn wir durchden Button ein Ereigniss (drücken) auslösen
    geht das über "onAction="#handleButtonAction" ,der ruft dann die entsprechende Funktion in der Controllerklasse auf.
    Geht auch hier über den namen, nur das hier in der FXML Datei vom Name der Controllerfunktion ein # steht.

    Jede Fxml Datei beschreibt ein Fenster und dazu gibt es genau eine Java Controller Classe.

    Worüber wir noch nicht gesprochen haben - das Java FX system starten:

    Code (Java):
    1. package javafxapphello;
    2.  
    3. import javafx.application.Application;
    4. import javafx.fxml.FXMLLoader;
    5. import javafx.scene.Parent;
    6. import javafx.scene.Scene;
    7. import javafx.stage.Stage;
    8.  
    9. /**
    10.  *
    11.  * @author susi
    12.  */
    13. public class JavaFXAppHello extends Application {
    14.  
    15.     @Override
    16.     public void start(Stage stage) throws Exception {
    17.         Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
    18.  
    19.         Scene scene = new Scene(root);
    20.  
    21.         stage.setScene(scene);
    22.         stage.show();
    23.     }
    24.  
    25.     /**
    26.      * @param args the command line arguments
    27.      */
    28.     public static void main(String[] args) {
    29.         launch(args);
    30.     }
    31.  
    32. }
    Hier wird eigendlich nur die Fxml Datei dem Loader übergeben und dann mit show wird das ganze angezeigt.

    Zum Schluss kommen wir zur Model Class die letzlich zwei Werte beinhaltet:
    Die Anrede und den namen des Angeredeten.

    Code (Java):
    1. package javafxapphello;
    2.  
    3. import javafx.beans.property.SimpleStringProperty;
    4. import javafx.beans.property.StringProperty;
    5.  
    6. public class XHello {
    7.  
    8.     private final StringProperty anrede;
    9.     private final StringProperty name;
    10.  
    11.     public XHello() {
    12.         this("Hallo","Welt");
    13.     }
    14.  
    15.     public XHello(String hi,String name) {
    16.         this.anrede = new SimpleStringProperty(hi);
    17.         this.name = new SimpleStringProperty(name);
    18.     }
    19.  
    20.     public void setAnrede(String hi) {
    21.        this.anrede.set(hi);
    22.     }
    23.  
    24.     public String getAnrede() {
    25.         return anrede.getValue();
    26.     }
    27.  
    28.     public StringProperty anredeProperty() {
    29.         return anrede;
    30.     }
    31.  
    32.     public void setName(String name) {
    33.         this.name.set(name);
    34.     }
    35.  
    36.     public String getName() {
    37.         return name.getValue();
    38.     }
    39.  
    40.     public StringProperty nameProperty() {
    41.         return name;
    42.     }
    43.  
    44.     @Override
    45.     public String toString() {
    46.         return (anrede + " " + name);
    47.     }
    48. }
    Ein Pojo Classe - mit geter und seter für jeden Inhalt. Was hier noch zusätzlich gemacht wird ist das ein String zur StringProperty
    aus der javaFX Package wird. Dies ist Unsere Model / Datenclasse die später noch in eine Liste gespeichert wird, um angezeigt werden zu können.

    Wenn wir nun das Programm starten wird unser Hello World Programm angezeigt - und die einzige Action die wir Auslösen können ist den Button
    zu drücken. Das machen wir auch mutig und lösen eine Action aus und wir landen in unserer Controller Class und es wird "handleButtonAction"
    aufgerufen. Dort wird einfach das Eingabefeld und den aktuellen Inhalt der Combobox einfach ausgelesen.
    Weder Combobox noch Eingabefeld lösen eine action aus. Die beiden ermittelten Stringwerte werden an eine neue Model Classe übergeben und die
    wird auf einer Liste gespeichert. Falls mehr wie 5 Einträge drauf sind wird einfach der älteste Eintrag gelöscht.

    Jetzt kommen wir zu Trick 17 - wir nutzen keine gewöhnliche ArrayList sondern die "FXCollections.observableArrayList();"
    Die Liste wird beobachtet (siehe auch Das Beobachter-Pattern (Observer/Observable zum Nachlesen)). Mal ganz einfach gesagt das FX System
    bekommt mit wenn sich beim Array etwas ändert - in unserem Fall ein neuer Eintrag.
    Im Hintergrund bekommt das System mit das sich was geändert hat und es wohl die angezeigte Liste neu anzeigen muss.
    Wir müssen nicht viel dazu tun - Programmtechnisch.

    In der FXML Datei wird mit "TableView" eine tabelle definiert und in der Tabelle zwei Spalten angelegt. Zu beachten ist das alles über FX:ID einen Namen bekommt (Ihr wisst schon wegen dem Zugriff von der Controller Class aus).
    Damit der automatische Update der Anzeige der Tabelle nachher auch funkoniert wenn die
    FXCollections.observableArrayList();
    eine änderung bekommt müssen wir noch ein paar Zeilen Code schreiben.
    "xanrede.setCellValueFactory(cellData -> (cellData.getValue().anredeProperty()));"

    Sorgt mal ganz kurz gesagt dafür das Daten in die anzuzeigene Tabellenzelle angezeigt werden.

    Ein kleine Kunstgriff habe ich noch bei der Combobox angewendet. In meinem Beispiel wird erst eine FXCollections direct in der FXML Datei angelegt
    und auch gleich mitString werten gefüllt. Was damit klar werden soll ist das letzlich jedes XML Elemt auch eine entsprechung als Java Classe hat.
    Wir erinnern uns wir könnten den Viev auch mit eine Javaclasse machen (wenn man will - aber warum sollte man :))
    Das Value Element legt eigendlich nur noch den Defaultwert fest.



    === Fortsetzung folgt ===

    Grafiken

    1. fx8-mvc.png