JavaFX Teil 1

JavaFX Teil 1 2018-02-02

No permission to download
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.
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:
<?xml version="1.0" encoding="UTF-8"?>


<?import javafx.collections.*?>

<?import java.lang.*?>

<?import java.util.*?>

<?import javafx.scene.*?>

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

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

<?import javafx.collections.*  ?>


<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">

    <children>
 
      <Button fx:id="button" layoutX="180.0" layoutY="95.0" onAction="#handleButtonAction" text="Click Me!" />
 
      <TextField fx:id="name" layoutX="251.0" layoutY="38.0" text="Welt" />
 
      <ComboBox fx:id="anrede" layoutX="58.0" layoutY="38.0" prefWidth="150.0">
     
       <items>
         
        <FXCollections fx:factory="observableArrayList">
             
         <String fx:value="Hallo" />
             
         <String fx:value="Moin" />
             
         <String fx:value="Tag" />
             
         <String fx:value="HuHu" />
         
        </FXCollections>

       </items>

       <value>
         
        <String fx:value="Hallo" />
     
       </value>
 
       </ComboBox>
 
        <TableView fx:id="table" layoutX="58.0" layoutY="149.0" prefHeight="200.0" prefWidth="350.0">
     
         <columns>
           
          <TableColumn fx:id="xanrede" prefWidth="175.0" text="Anrede" />
         
          <TableColumn fx:id="xname" prefWidth="175.0" text="Text" />
     
         </columns>

        </TableView>
 
  </children>

</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:
package javafxapphello;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.AccessibleAttribute;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

/**
 *
 * @author susi
 */
public class FXMLDocumentController implements Initializable {

    private Label label;
    @FXML
    private Button button;
    @FXML
    private ComboBox<?> anrede;
    @FXML
    private TextField name;
    @FXML
    private TableView<XHello> table;
    @FXML
    private TableColumn<XHello, String> xanrede;
    @FXML
    private TableColumn<XHello, String> xname;

    private ObservableList<XHello> data = FXCollections.observableArrayList();

    @FXML
    private void handleButtonAction(ActionEvent event) {
        String inanrede = anrede.getSelectionModel().getSelectedItem().toString();
        String inname = name.getText();
        System.out.println("Click " + inanrede + " " + inname);
        data.add(new XHello(inanrede, inname));
        if (data.size() > 5) {
            data.remove(0);
        }

        if (!data.isEmpty()) {
            System.out.println(data.toString());
        }
 
    }

    @FXML
    public void initialize() {

        xanrede.setCellValueFactory(cellData -> (cellData.getValue().anredeProperty()));
        xname.setCellValueFactory(cellData -> (cellData.getValue().nameProperty()));

        table.setItems(data);
        System.out.println("init fxml");
    }

   @Override
   public void initialize(URL url, ResourceBundle rb) {
        // TODO
        System.out.println("init override");
        initialize();
    }

}

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:

Java:
package javafxapphello;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 *
 * @author susi
 */
public class JavaFXAppHello extends Application {
 
    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
 
        Scene scene = new Scene(root);
 
        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
 
}

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.

Java:
package javafxapphello;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class XHello {
 
    private final StringProperty anrede;
    private final StringProperty name;
 
    public XHello() {
        this("Hallo","Welt");
    }
 
    public XHello(String hi,String name) {
        this.anrede = new SimpleStringProperty(hi);
        this.name = new SimpleStringProperty(name);
    }
 
    public void setAnrede(String hi) {
       this.anrede.set(hi);
    }
 
    public String getAnrede() {
        return anrede.getValue();
    }
 
    public StringProperty anredeProperty() {
        return anrede;
    }
 
    public void setName(String name) {
        this.name.set(name);
    }
 
    public String getName() {
        return name.getValue();
    }
 
    public StringProperty nameProperty() {
        return name;
    }
 
    @Override
    public String toString() {
        return (anrede + " " + name);
    }
}

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 ===
  • fx8-mvc.png
    fx8-mvc.png
    13,8 KB · Aufrufe: 2.470
  • Gefällt mir
Reaktionen: sheel
Autor
melmager
Downloads
0
Aufrufe
4.676
First release
Last update
Bewertung
0,00 Stern(e) 0 Bewertungen

More resources from melmager

Zurück