Der letzte Beitrag meiner Blog-Serie „JavaFX mit FXML programmieren“ umfasst das Thema DataBinding. Als DataBinding bezeichnet man die Verbindung zweier Variablen. Dabei kann die Verbindung unidirektional (in eine Richtung) oder bidirektional (in beider Richtungen) sein.
Allgemein werden DataBindings typischerweise im Viewmodel verwendet und in Form von Property-Variablen realisiert. Adam Bien hat dafür ein sehr kurzes und einfaches Tutorial geschrieben:
Wer Adam Bien kennt, weis dass er nicht um den heißen Brei rumerzählt. Taten statt Worte;-)
Hier ist seine Erklärung von DataBinding in Form eines JUnit Tests:
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class StringDataBindingTest { @Test public void bindUnidirectionally(){ String expected = "DukeFX"; StringProperty from = new SimpleStringProperty(); StringProperty to = new SimpleStringProperty(); to.bind(from); from.set(expected); String actual = to.get(); assertThat(actual,is(expected)); } }
Was haben wir gelernt? Wenn man zwei Property-Variablen mit der Methode „.bind()“ oder „.bindBidirectional()“ verbindet, verweisen beide Variablen auf den gleichen Inhalt. Vergleichbar ist das Verhalten mit Pointern in C++:
int expected = 5; int* from, to; from = &expected; to = &expected;
Der Unterschied zwischen „.bind()“ und „.bindBidirectional()“ ist, dass bei „x.bind(y)“ eine „java.lang.RuntimeException: A bound value cannot be set.“ Exception fliegt, wenn „x“ geändert wird.
Neben unidirektional und bidirektional, muss man auch die richtige Property einsetzen. Properties sind eigentlich Interfaces, die nicht erzeugt werden können. Hierfür gibt es die Simple*-Klassen:
Ihr habt die Theorie überstanden! Nun geht es in die Praxis – mein Beispiel-Programm besteht aus 3 Dateien:
Am wichtigsten ist die Controller-Klasse!!!
import java.io.IOException;
import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage;
public class Start extends Application {
public static void main(String[] args) { launch(args); }
@Override public void start(Stage primaryStage) throws IOException { // +++++++++++++++++++++++++++++++++++++++++++++ // Layout // +++++++++++++++++++++++++++++++++++++++++++++
// FXML-Datei laden! Parent root = FXMLLoader.load(getClass().getResource("maske.fxml"));
// Szene Scene scene = new Scene(root);
// +++++++++++++++++++++++++++++++++++++++++++++ // Stage konfigurieren // +++++++++++++++++++++++++++++++++++++++++++++
// Titel setzen primaryStage.setTitle("AxxG – FXML Beispiel");
// Szene setzen primaryStage.setScene(scene); primaryStage.sizeToScene();
// Stage anzeigen primaryStage.show(); } } [/java]
<?import java.lang.*?> <?import java.util.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.paint.*?> <?import javafx.scene.text.*?>
<GridPane fx:id="grid" alignment="CENTER" hgap="10.0" vgap="10.0" xmlns:fx="http://javafx.com/fxml" fx:controller="de.axxg.fxml.Controller"> <children> <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Hallo AxxG-Leser" GridPane.columnIndex="0" GridPane.rowIndex="0"> <font> <Font name="Tahoma" size="20.0" /> </font> </Text> <Label text="Vorname:" GridPane.columnIndex="0" GridPane.rowIndex="1" /> <Label text="Nachname:" GridPane.columnIndex="0" GridPane.rowIndex="2" /> <TextField fx:id="vornameTF" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" /> <TextField fx:id="nachnameTF" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" /> <HBox GridPane.columnIndex="1" GridPane.rowIndex="4"> <children> <Button fx:id="addBtn" mnemonicParsing="false" text="eintragen" onAction="#go"/> <Button fx:id="okBtn" mnemonicParsing="false" text="Alle anzeigen" onMouseEntered="#enter" /> </children> </HBox> </children> <padding> <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" /> </padding> </GridPane> [/xml]
In Zeile 15 initialisiere ich die StringProperty mit dem Wert „Alexander“. Dieser Wert wird dann in der Oberfläche angezeigt.
In Zeile 23 bzw. 28 definiere ich die DataBindings. Drückt man jetzt auf einen der Buttons wird jeweils einer der Werte geändert und beide ausgegeben.
package de.axxg.fxml; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.input.MouseEvent; public class Controller { @FXML private TextField vornameTF; private StringProperty vorname = new SimpleStringProperty("Alexander"); // Diese Methode wird nach dem Inject aufgerufen! public void initialize(){ // nur vorname kann vornameTF aktuallisieren // ansonsten kommt eine Exception // java.lang.RuntimeException: A bound value cannot be set. vornameTF.textProperty().bind(vorname); // oder // Beide Seiten aktuallisieren sich vornameTF.textProperty().bindBidirectional(vorname); } @FXML protected void go(ActionEvent event) { vorname.setValue("Peter"); System.out.println("Stringprop.: " + vorname.getValue()); System.out.println("Textfeld: " + vornameTF.getText()); } @FXML protected void enter(MouseEvent event) { vornameTF.setText("Die Maus ist über den Button!"); System.out.println("Stringprop.: " + vorname.getValue()); System.out.println("Textfeld: " + vornameTF.getText()); } }
Die Ausgabe könnte so aussehen:
Stringprop.: Peter Textfeld: Peter Stringprop.: Die Maus ist über den Button! Textfeld: Die Maus ist über den Button! Stringprop.: Die Maus ist über den Button! Textfeld: Die Maus ist über den Button! Stringprop.: Peter Textfeld: Peter
Für Faule gibt es hier ein fertiges Projekt…
Copyright © 2013 AxxG – Alexander Gräsel
Kommentar verfassen