30.07.2013

Tipps & Tricks – JavaFX und Eclipse 4

Teil 2 – Controller

Unter anderem ermöglicht InjectingFXMLLoader es, über den Dependecy Injection Mechanismus von Eclipse 4 Referenzen auf GUI Elemente aus der FXML Datei an Membervariablen in einer Controller Klasse zuzuweisen. Im Controller können dann Inhalte der GUI Elemente ausgelesen oder geändert werden. Die Member der Controller Klasse müssen lediglich mit der Annotation @FXML ausgezeichnet werden:

package application.parts;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;

public class LoginController {

@FXML
private TextField textBoxLogin;

@FXML
private PasswordField textBoxPassword;

@FXML
private Label actiontarget;

@FXML
private Button buttonLogin;

private boolean isLoggedIn = false;

@FXML
protected void handleSubmitButtonAction(ActionEvent event) {
if(isLoggedIn) {
isLoggedIn = false;
textBoxLogin.setDisable(false);
textBoxPassword.setDisable(false);
actiontarget.setText("");
buttonLogin.setText("Sign In");
textBoxLogin.requestFocus();
} else {
if(textBoxLogin.getText().equals("admin") &&
textBoxPassword.getText().equals("admin")) {
isLoggedIn = true;
textBoxLogin.setDisable(true);
textBoxPassword.setDisable(true);
actiontarget.setText("You are logged in as: " +
textBoxLogin.getText());
buttonLogin.setText("Logout");
} else {
actiontarget.setText("Invalid login or passwort. " +
"Try 'admin / admin'.");
textBoxLogin.requestFocus();
}
}
}

@FXML
protected void handleLoginNameEnterPressed(ActionEvent event) {
actiontarget.setText("");
textBoxPassword.requestFocus();
}

}

In der FXML Datei müssen die IDs der GUI Elemente dabei mit dem Attribut fx:id spezifiziert werden:

<TextField fx:id="textBoxLogin"
GridPane.columnIndex="1" GridPane.rowIndex="1"
onAction="handleLoginNameEnterPressed(event);" />

Diese IDs müssen genau den Namen der Membervariablen der Controller Klasse entsprechen.

Alternativ besteht auch die Möglichkeit, den Controller in JavaScript zu implementieren und direkt mit in die FXML Datei zu integrieren. Eine vollständige FXML Datei könnte dann z.B. so aussehen:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<?language javascript?>

<VBox xmlns:fx="http://javafx.com/fxml" minWidth="600" minHeight="300">
<!-- Comment next line in to use the java class based controller -->
<!-- fx:controller="application.parts.LoginController" -->
<fx:script>
/* <![CDATA[ */
// CDATA masks the '&&' special chars below,
// thus avoids having to write &amp;&amp;

var isLoggedIn = false;

function handleLoginNameEnterPressed() {
actiontarget.setText("");
textBoxPassword.requestFocus();
}

function handleSubmitButtonAction() {
if(isLoggedIn) {
isLoggedIn = false;
textBoxLogin.setDisable(false);
textBoxPassword.setDisable(false);
actiontarget.setText("");
buttonLogin.setText("Sign In");
textBoxLogin.requestFocus();
} else {
if(textBoxLogin.getText().equals("admin") &&
textBoxPassword.getText().equals("admin")) {
isLoggedIn = true;
textBoxLogin.setDisable(true);
textBoxPassword.setDisable(true);
actiontarget.setText("You are logged in as: " +
textBoxLogin.getText());
buttonLogin.setText("Logout");
} else {
actiontarget.setText("Invalid login or passwort. " +
"Try 'admin / admin'.");
textBoxLogin.requestFocus();
}
}
}
/* ]]> */
</fx:script>

<GridPane xmlns:fx="http://javafx.com/fxml" alignment="center"
hgap="14" vgap="10" minWidth="600" prefWidth="600"
minHeight="300" prefHeight="300">
<!-- Make grid lines visible for debugging: -->
<!-- gridLinesVisible="true" -->

<padding>
<Insets top="25" right="25" bottom="10" left="25" />
</padding>

<HBox alignment="center" GridPane.columnIndex="0"
GridPane.rowIndex="0" GridPane.columnSpan="2">

<Text id="welcome-text" text="Welcome to JavaFX" />
</HBox>

<Label text="User Name:" GridPane.columnIndex="0"
GridPane.rowIndex="1" />

<TextField fx:id="textBoxLogin" GridPane.columnIndex="1"
GridPane.rowIndex="1"
onAction="handleLoginNameEnterPressed(event);" />

<Label text="Password:" GridPane.columnIndex="0"
GridPane.rowIndex="2" />

<PasswordField fx:id="textBoxPassword"
GridPane.columnIndex="1" GridPane.rowIndex="2"
onAction="handleSubmitButtonAction(event);" />

<HBox spacing="10" alignment="bottom_right"
GridPane.columnIndex="1" GridPane.rowIndex="4">

<Button fx:id="buttonLogin" text="Sign In"
onAction="handleSubmitButtonAction(event);" />
</HBox>

<Label fx:id="actiontarget" GridPane.columnIndex="1"
GridPane.rowIndex="6" />
</GridPane>

</VBox>

Damit wird die selektive Integration von JavaFX in Eclipse RCP Anwendungen deutlich vereinfacht und den Entwicklern von RCP Anwendungen stehen die vielen neuen Möglichkeiten, die diese Technologie bietet, nun in vollem Umfang zur Verfügung.

Der Beispielcode wurde mit Eclipse Juno 4.2.2 und Java Platform (JDK) 7 Update 25 getestet.

Einen Link zum Download des Beispielcodes inclusive der kompletten Eclipse Projekt Struktur finden Sie übrigens am Ende des nächsten Artikels.

Mein besonderer Dank geht an

  • die Autoren des efxclipse Plugins von BestSolution.at für ihren hervorragenden Beitrag zur Eclipse Open Source Community
  • die Autoren des JavaFX Tutorials von Oracle für ihre hervorragende Einführung und die ausführlichen Codebeispiele