My JavaOne 2014 Sessions

Uh, as a featured speaker I will give a lot sessions this year at JavaOne. This is the main reason why I found no time to blog about JavaFX the last weeks ;)
If you will attend JavaOne I will be happy to see you at one of my session:

Enterprise JavaFX
Venue / Room: Hilton – Plaza A
Date and Time: 9/29/14, 16:00 – 17:00

DataFX: From External Data to a UI Flow and Back
Venue / Room: Hilton – Plaza B
Date and Time: 9/29/14, 11:00 – 12:00

Smart UIs for Mobile and Embedded in JavaFX
Venue / Room: Hilton – Plaza B
Date and Time: 9/30/14, 19:00 – 19:45

The JavaFX Community and Ecosystem
Venue / Room: Hilton – Plaza B
Date and Time: 9/30/14, 11:00 – 12:00

Extreme GUI Makeover
Venue / Room: Hilton – Yosemite B/C
Date and Time: 10/1/14, 13:00 – 14:00

Test-Driven Development with JavaFX
Venue / Room: Hilton – Plaza B
Date and Time: 10/1/14, 11:30 – 12:30

Maybe I can organize some “Mastering JavaFX 8 Controls” books for the audience :)

DataFX Tutorial 5

This is the last tutorial about the basic flow and action API of DataFX. I plan to start a second tutorial series about injection and resources in DataFX. But in those parts of DataFX 8 the APIs aren’t final at the moment. So it will sadly take one or two month before I can continue the tutorials. An overview of all tutorials can be found here.

In this tutorial I want to show how flows can be interlaced. Therefore we create a new variation of the wizard app that was shown in tutorial 4. The application should provide a simple wizard with some steps. Here is an overview how the dialogs and the flow where defined in tutorial 4:
flow3
Once the application is finished it will look like the own that was created last time:
views
The last implementation of the wizard has one weak point in its architecture: Each step of the wizard is defined as a view in a flow. The view contains the complete view of the application and so also the action toolbar is part of each view. In the last tutorial we extracted the toolbar in a separate FXML file but the controls were created for each view individually. But the toolbar has the same functionality on each view and normally it should be one single instance and only the view in the center of the application should change:
tut5-2
This more logical structure is part of this tutorial and by using interlaced flows the favored behavior can be created. As always we will start by creating the FXML files for the application. Because the top views of the wizard should be defined in a separate flow there will be FXML files for the views that only contain the content of one wizard step. Here is an example for the first step:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>

<Label text="Welcome!">
    <font>
        <Font size="24.0"/>
    </font>
</Label>

As you can see the FXML is much shorter than in the other tutorials. It only contains the Label that will be shown on the first view of the wizard.
For the toolbar we can use the same FXML file as in tutorial 4:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<HBox alignment="CENTER_RIGHT" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="12.0" style="-fx-background-color: darkgray;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
  <Button fx:id="backButton" mnemonicParsing="false" text="Back" />
  <Button fx:id="nextButton" mnemonicParsing="false" text="Next" />
  <Button fx:id="finishButton" mnemonicParsing="false" text="Finish" />
</children>
<padding>
  <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
</padding>
</HBox>

The FXML defines a HBox that include the 3 action buttons.
Once this is done the application is defined by the following FXML files:
tut5-3
As a next step a master FXML file is needed that includes the different components. As shown in then last tutorial the fx:include tag can be used to include FXML files within each other. Because the toolbar will be static component and the bottom of the application it can be included in the main FXML file. The wizard views on the top will change. Therefore we add a StackPane to the main FXML file that will hold the views. Here is the FXML file:

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

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<BorderPane prefHeight="240.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <bottom>
        <fx:include source="actionBar.fxml"/>
    </bottom>
    <center>
        <StackPane fx:id="centerPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
        </StackPane>
    </center>
</BorderPane>

The StackPane that will hold the central views of the wizard is defined by the unique id “centerPane”. Once this is done all FXML files are finished and we can start to code in JavaFX. Let’s take a look at the general structure of the application before we start to define the needed flows. As said the application will contain two flows that are interlaced. Here is a graphic that shows the structure:
tut5-1
We will start with the view controller classes for all the wizard steps. Because the action toolbars isn’t part of this views anymore and they contain only a label the code is very short :) Here is the class for the first view controller:

@FXMLController("wizardView1.fxml")
public class WizardView1Controller {
}

Easy, isn’t it? The class is only needed to specify the view in the Java code and create the binding between the class and the FXML file. As you can see in the code of the example all other controller classes will look like this.
As a next step we will create the flow of for these views. Here we only need to link the view for navigation. Therefore the definition of the flow will look like this:

Flow flow = new Flow(WizardView1Controller.class).
                withLink(WizardView1Controller.class, "next", WizardView2Controller.class).
                withLink(WizardView2Controller.class, "next", WizardView3Controller.class).
                withLink(WizardView3Controller.class, "next", WizardView4Controller.class).
                withLink(WizardView4Controller.class, "next", WizardView5Controller.class);

Ok, but where should this flow be defined. Until now all flows were defined in the main class of the application but this flow is an inner flow that will be part of another flow. To better understand how this can be achieved we should have a look at the main class of the application:

public class Tutorial5Main  extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Flow flow  = new Flow(WizardController.class);

        FlowHandler flowHandler = flow.createHandler();

        StackPane pane = flowHandler.start(new DefaultFlowContainer());
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();
    }
}

As in all other tutorials a flow is defined and started in the application class. But as you can see this flow defines only one start view that is defined by its controller class (WizardController). This class defines the controller of the main view of the application that is defined by the main FXML file as already shown. The flow that defines the steps of the wizard will be an inner flow in this view. Therefore the inner flow is defined in the WizardController controller class:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    private StackPane centerPane;

    private FlowHandler flowHandler;

    @PostConstruct
    public void init() throws FlowException {
        Flow innerFlow = new Flow(WizardView1Controller.class).
                withLink(WizardView1Controller.class, "next", WizardView2Controller.class).
                withLink(WizardView2Controller.class, "next", WizardView3Controller.class).
                withLink(WizardView3Controller.class, "next", WizardView4Controller.class).
                withLink(WizardView4Controller.class, "next", WizardView5Controller.class);

        flowHandler = innerFlow.createHandler();
        centerPane.getChildren().add(flowHandler.start(new AnimatedFlowContainer(Duration.millis(320), ContainerAnimations.ZOOM_IN)));
    }
}

Here all the steps of the wizard are added and linked to the inner flow. As you can see in the code the flow is started in a difference way than it was done several times before. A FlowHandler instance is created and this instances is started. This is wrapped in the startInStage() method of the Flow class that was used in all the other tutorials. The FlowHandler instance represents a running flow. The Flow class only defines the structure of a flow and can be started several times in parallel. Each running flow instance is defined by a FlowHandler instance and The FlowHandler instance can be used to interact with the flow. In addition the start(…) method of the FlowHandler class needs a FlowContainer as parameter. The FlowContainer provides a parent node in that the flow will be shown. In this case we use an AnimatedFlowContainer that provides animations whenever the flow navigates to a different view. We will see the animations at the end of the tutorial. The flow will be placed in the centerPane that is defined by FXML and injected in the controller by using the @FXML annotation.
As said the FlowHandler class provides methods to interact with the flow. We will use this methods in the tutorial to call the “next”, “back” and “finish” actions of the flow. To do so we need the action buttons that can easily be injected in the controller class:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;
    @FXML
    @ActionTrigger("finish")
    private Button finishButton;
    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    @FXML
    private StackPane centerPane;

    //init()...
}

All the Buttons are annotated by the @ActionTrigger annotation and will therefore trigger actions in the flow. But they won’t trigger actions in the inner flow because they are defined in the global flow that is defined in the main class. To handle this actions we will create some methods that are annoted by the @ActionMethod() annotation. As seen in tutorial 1 these methods will be called whenever the action with the matching unique id is called:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;

    @FXML
    @ActionTrigger("finish")
    private Button finishButton;

    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    //init()...

    @ActionMethod("back")
    public void onBack() throws VetoException, FlowException {
    }

    @ActionMethod("next")
    public void onNext() throws VetoException, FlowException {
    }

    @ActionMethod("finish")
    public void onFinish() throws VetoException, FlowException {
    }
}

In these methods the FlowHandler instance of the inner flow can be used to interact with the inner flow and navigate to new views or trigger actions. In addition some logic is needed to enable and disable the action buttons depending on the state of the inner flow. Here is the complete code of the WizardController class with the final implementation of these methods:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;

    @FXML
    @ActionTrigger("finish")
    private Button finishButton;

    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    @FXML
    private StackPane centerPane;

    private FlowHandler flowHandler;

    @PostConstruct
    public void init() throws FlowException {
        Flow flow = new Flow(WizardView1Controller.class).
                withLink(WizardView1Controller.class, "next", WizardView2Controller.class).
                withLink(WizardView2Controller.class, "next", WizardView3Controller.class).
                withLink(WizardView3Controller.class, "next", WizardView4Controller.class).
                withLink(WizardView4Controller.class, "next", WizardView5Controller.class);

        flowHandler = flow.createHandler();
        centerPane.getChildren().add(flowHandler.start(new AnimatedFlowContainer(Duration.millis(320), ContainerAnimations.ZOOM_IN)));

        backButton.setDisable(true);
    }

    @ActionMethod("back")
    public void onBack() throws VetoException, FlowException {
        flowHandler.navigateBack();
        if(flowHandler.getCurrentViewControllerClass().equals(WizardView1Controller.class)) {
            backButton.setDisable(true);
        } else {
            backButton.setDisable(false);
        }
        finishButton.setDisable(false);
        nextButton.setDisable(false);
    }

    @ActionMethod("next")
    public void onNext() throws VetoException, FlowException {
        flowHandler.handle("next");
        if(flowHandler.getCurrentViewControllerClass().equals(WizardView5Controller.class)) {
            nextButton.setDisable(true);
            finishButton.setDisable(true);
        } else {
            nextButton.setDisable(false);
        }
        backButton.setDisable(false);
    }

    @ActionMethod("finish")
    public void onFinish() throws VetoException, FlowException {
        flowHandler.navigateTo(WizardView5Controller.class);
        finishButton.setDisable(true);
        nextButton.setDisable(true);
        backButton.setDisable(false);
    }
}

Once this is done you can start the flow and navigate through all the steps of the wizard. Thanks to the AnimatedFlowContainer container each link will be animated:

As I said this is the final tutorial about the Flow API. From our point of view the API is finshed and we only plan to polish some stuff and add documentation. But we plan to release DataFX 8 at JavaOne and so there is still some time to change things. So if you have any questions, feedback or improvements please let us now. The next step is the CDI part of DataFX and once the APIs are stable I will continue this tutorial series.

AeroFX: It’s getting closer

As shown in the sneak peek last week a new JavaFX theme is in development. Matthias Meidinger is creating this skin as part of his bachelor thesis and since the last week he made some big improvements. So I think it’s time for a new preview of the upcoming JavaFX theme. The following pics show dialogs that use the AeroFX skin:

j1
j5

Currently all the animations for focused controls and custom SVG parts like the check mark for checkboxes are in development. Once this parts are done I will post a new update. We hope you like this work ;)

Sneak Peek: AeroFX

Last year we released AquaFX as a JavaFX 8 theme that emulates a native Look for JavaFX applications on Mac OS. Applications that use AquaFX may look like this:
AquaFX-specials
A complete documentation of AquaFX can be found on its project page and at our blog.

Today I want to show a sneak peek on a new project called AeroFX. AeroFX is a theme for JavaFX that will emulate the native look of the Windows interface. AeroFX will use the same architecture as AquaFX so that developers can easily switch between the skins or support both on different operation systems. The skin of AeroFX will look perfect on Windows 7. At the moment AeroFX is in a very early stage and contains only skins for some basic controls. Because AeroFX is developed as a bachelor thesis the source code can’t be opened at the moment. But once the work is finished AeroFX will be provided under the same open source license as AquaFX. Here is the first screenshot of AeroFX:

aeroFX sneak peek

DataFX Tutorial 4

This is the 4th tutorial about navigation with DataFX and the DataFX flow API. An overview of all tutorials can be found here. In this tutorial I will show how you can manage the action handling and navigation of a flow from outside of the flow. To do so we will refactor the wizard that was created in tutorial 3.

As described in tutorial 3 the wizard will be composed of some views that define the steps of the wizard. In addition a toolbar with some buttons is placed on the bottom. The views will look like this:
views
All views of the wizard are linked by a navigation model. In tutorial 3 this was created by directly in the view controller classes so each view defines its navigation and actions. In this tutorial we will use the second approach that DataFX provides: All views doesn’t know anything about there action and navigation model. Instead of this the actions are defined extern. The navigation and action behavior will be the same as in tutorial 3. Here is a short overview about the links between the views of the wizard:
flow3
As always we want to start by defining the views in FXML. Because the toolbar will look the same on each view we can extract it in a seperate FXML file. As shown in tutorial 3 a FXML file can included in another one by using the fx:include tag. Here is the FXML definition of the toolbar:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<HBox alignment="CENTER_RIGHT" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="12.0" style="-fx-background-color: darkgray;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
  <Button fx:id="backButton" mnemonicParsing="false" text="Back" />
  <Button fx:id="nextButton" mnemonicParsing="false" text="Next" />
  <Button fx:id="finishButton" mnemonicParsing="false" text="Finish" />
</children>
<padding>
  <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
</padding>
</HBox>

The definition of the toolbar is the same as in the last tutorial. The definition of the wizard steps is the same, too. Here is a FXML definition of one step:

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

<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<BorderPane prefHeight="240.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
      <bottom>
          <fx:include source="actionBar.fxml" />
      </bottom>
      <center>
          <StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
              <children>
                  <Label text="Welcome!">
                      <StackPane.margin>
                          <Insets bottom="32.0" left="32.0" right="32.0" top="32.0" />
                      </StackPane.margin>
                      <font>
                          <Font size="24.0" />
                      </font>
                  </Label>
              </children>
          </StackPane>
      </center>
</BorderPane>

As a next step we need view controller classes for all views in the wizard. As a first step we will create empty classes that are annoted with the FXMLController annotation:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller {

}

All the actions of the wizard will be triggered by the toolbar. Because the toolbar is defined on each view we can create an abstract class for the toolbar components that can be used as a superclass for the view controller classes:

public class AbstractWizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;

    @FXML
    @ActionTrigger("finish")
    private Button finishButton;

    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    public Button getBackButton() {
        return backButton;
    }

    public Button getFinishButton() {
        return finishButton;
    }

    public Button getNextButton() {
        return nextButton;
    }
}

All view controller classes can now extend the class:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller extends AbstractWizardController {

}

Mainly the same structure was created in tutorial 3 but here we have one big different. In Chapter 3 the next button can’t be defined in the super class because it was annotated with the @LinkAction annotation. This must be done in each controller class because the target of the navigation must be defined as an parameter of the annotation definition (see tutorial 3 for a more detailed description). As already mentioned we want to extract the action handling in this tutorial. So all buttons can be defined and injected in the AbstractWizardController class. As you can see in the code the buttons are annoted by the @ActionTrigger annotation that was already used in some other tutorials. By using this annotation an action id is defined and the flow will trigger the action that is specified by this id each time the button will be pressed.

The last thing that is missing is the main class. This class will look different to the main classes of the first three tutorials. Here we want to define all the actions of the wizard and link its views. To do so we start with a simple class and show the flow. You should know this kind of class from the other tutorials:

public class Tutorial4Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(WizardStartController.class).startInStage(primaryStage);
    }
}

As a next step we want to bind all the different views and create a navigation model. To do so the Flow class contains some methods that can be used to define links between views of the flow. The Flow class is defined as a fluent API and navigation links or action can be simply added to it. Here is a example for a flow with one link:

new Flow(View1Controller.class).
withLink(View1Controller.class, "link-id", View2Controller.class).
startInStage(primaryStage);

In the flow a link from view 1 to view 2 is defined. Both views are specified by their controller classes (View1Controller.class and View2Controller.class) and the link is defined by the unique id “link-id”. Once this is done you can simply add the @ActionTrigger(“link-id”) annotation to a defined node in the View1Controller class. DataFX will register the navigation to view 2 for this node. So whenever the node is clicked the navigation will be transformed.
For the current example the code of the main class will look like this:

public class Tutorial4Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(WizardStartController.class).
                withLink(WizardStartController.class, "next", Wizard1Controller.class).
                withLink(Wizard1Controller.class, "next", Wizard2Controller.class).
                withLink(Wizard2Controller.class, "next", Wizard3Controller.class).
                withLink(Wizard3Controller.class, "next", WizardDoneController.class).
                withGlobalBackAction("back").
                withGlobalLink("finish", WizardDoneController.class).
                startInStage(primaryStage);
    }
}

Next to the withLink(…) method two additional methods of the Flow class are used in the code. The withGlobalLink(…) method defines a navigation action that will be registered for each view in the flow. So the @ActionTrigger(“finish”) annotation can be used in each view and will navigate to the last view of the wizard. For each action type that can be registered to a DataFX flow the Flow class provides methods to register the action for only one view or as a global action for all views. This is done for the back action, too. The “back” button is visible on each view of the wizard and therefore the withGlobalBackAction(“back”) method is used here. So whenever a action with the id “back” is triggered in any view of the flow a back action will be called. This is exactly the same as adding a @BackAction annotation to the node that should trigger the back action.
All these methods add a action to the DataFX flow. A action is defined by the interface org.datafx.controller.flow.action.FlowAction and all the shown methods will internally call the Flow.addActionToView(Class controllerClass, String actionId, FlowAction action) method that will add a action instance for the defined id to a specific view. Methods that add global actions will call Flow.addGlobalAction(String actionId, FlowAction action) internally. As you can see even custom actions can be added to a flow by simply implementing the FlowAction interface. DataFX contains a set with all the most important actions types that can be added to a flow or a specific view. The following figure shows the inheritance of the FlowAction interface:
ACTION-uml
Some of the actions in the diagram will be handled in future tutorials. First I will only explain the basic action types:

  • FlowLink defines a link to another view in the flow. In the example instances of this class will be registered to the flow whenever the withLink(…) method is called.
  • FlowBackAction handles a back navigation in the flow. This is the same as using the @BackAction annotation in a view controller
  • FlowTaskAction will execute a task that is defined as a Runnable on the Platform Application Thread. We will see an example of the action type after this overview.
  • FlowAsyncTaskAction will execute a task that is defined as a Runnable on a background thread.
  • FlowMethodAction will call a method in the given view. The same result can be created by using the @ActionMethod annotation as shown in the first tutorial.

As you can see in this overview all the actions that were created by using annotations in the previous tutorials can be defined directly for the flow. By doing so a controller class doesn’t need to now how an action is implemented. It must only now the specific id of the action and which node should trigger the action. This structure can be very helpful if default views should be used in multiple flows or if controller classes and / or action classes are part of different modules that don’t depend on each other. Let’s think about the following structure:
Bildschirmfoto 2014-06-08 um 22.03.20
In this example the ViewController1.class, ViewController2.class and CustomAction.class don’t know each other. With the help of the DataFX flow API you can simply combine them by using:

new Flow(View1Controller.class).
withLink(View1Controller.class, "link-id", View2Controller.class).
withAction(View2Controller.class "callAction", new CustomAction()).
startInStage(primaryStage);

As a last step I want to extent the example application and add a help output. This should be a global action that prints some help on the console. To do so the action is registered for the flow:

new Flow(WizardStartController.class).
withLink(WizardStartController.class, "next", Wizard1Controller.class).
...
withGlobalTaskAction("help", () -> System.out.println("## There is no help for the application :(  ##")).
startInStage(primaryStage);

As you can see in the code you can simply pass a lambda expression as a action to the flow because the FlowTaskAction class that is used internally here defines the action as a Runnable that is a function interface since Java8.
Once this is done the action can be triggered in any view:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller extends AbstractWizardController {

    @FXML
    @ActionTrigger("help")
    private Hyperlink helpLink;
}

When looking at the source code of the tutorial you will see that the “help” action isn’t triggered in all views. That is no problem for DataFX. A global action mustn’t be called in each view and even a normal action mustn’t be called in the defined controller. The API only defines that there is an action with the given id that could be called. For this last step a hyperlink tag is added to the FXML files. Here is a screenshot of the final wizard:
dialog-desc

DataFX Tutorial 3

In this tutorial I want to show how a wizard dialog can be created with DataFX. This example depends on 2 other tutorials that can be found here and here.

The wizard that will be created in this tutorial contains 5 different views that are linked to each other:
flow1
Next to the “next” action that navigates to the following view the wizard should support a “back” action that navigates to the last displayed view and a “finish” action that directly navigates to the last view:
flow3
Note: The last diagram doesn’t contain all possible links. Because the “back” action navigates always the last visible view you could navigate directly from the last view to the first view if the “finish” action was triggered in the first view and then the back action is triggered.

As shown in the other tutorials we will start the the view layout and generate all views by using FXML and Scene Builder 2. All the views of the wizard will contain a toolbar with some buttons to trigger the defined action. Here are some previews how the views will look like:
views
Thanks to FXML we don’t need to implement the toolbar for every view. For this purpose FXML provides the fx:include tag that can be used to interleave vxml defined views. So we can define the toolbar as a separate FXML file:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<HBox alignment="CENTER_RIGHT" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="12.0" style="-fx-background-color: darkgray;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
  <Button fx:id="backButton" mnemonicParsing="false" text="Back" />
  <Button fx:id="nextButton" mnemonicParsing="false" text="Next" />
  <Button fx:id="finishButton" mnemonicParsing="false" text="Finish" />
</children>
<padding>
  <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
</padding>
</HBox>

As you can see the buttons for the 3 defined actions (“back”, “next” and “finish”) are defined in the toolbar. As described in the earlier tutorials these components can be injected in the controller class instances by using the @FXML annotation and a field name that matches the value of the fx:id attribute.
The FXML of the toolbar (actionBar.fxml) can now included in all the FXML files that defines the different views of the wizard. Here is the code of the first view as an example:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>

<BorderPane prefHeight="240.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <bottom>
        <fx:include source="actionBar.fxml"/>
    </bottom>
    <center>
        <StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
            <children>
                <Label text="This is the first step.">
                    <StackPane.margin>
                        <Insets bottom="32.0" left="32.0" right="32.0" top="32.0"/>
                    </StackPane.margin>
                    <font>
                        <Font size="24.0"/>
                    </font>
                </Label>
            </children>
        </StackPane>
    </center>
</BorderPane>

As you can see the toolbar is integrated in the bottom of the central BorderPane.
Once all FXML files are created we can start to create the view controller as described in the earlier tutorials. Therefore we create a Java class for each view and bind the class to the corresponding FXML file by using the @FXMLController annotation:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller {

}

When looking at the @FXMLController annotation of the class you can find a new feature. Next to the fxml file that defines the view of the wizard step a title attribute is added. This defines the title of the view. Because the wizard will be added to a Stage by using the Flow.startInStage() method (see tutorial 1) the title of the flow is automatically bound to the window title of the Stage. So whenever the view in the flow changes the title of the application window will change to the defined title of the view. As you will learn in future tutorial you can easily change the title of a view in code. In addition to the title other metadata like a icon can be defined for a view or flow.
As a next step the buttons of the toolbar should be injected in the controller classes and the specific actions for them should be defined. Here a new annotation will be introduced: By using the @BackAction annotation the flow will automatically handle an action that navigates to the last visible view. The annotation can be used like the @ActionTrigger and @LinkAction annotations that were introduced in tutorial 1 and 2. Therefore the controller class for the a view in the wizard could be defined like this:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller {

    @FXML
    @LinkAction(Wizard2Controller.class)
    private Button nextButton;

    @FXML
    @BackAction
    private Button backButton;

    @FXML
    @LinkAction(WizardDoneController.class)
    private Button finishButton;

}

When all controllers would be designed like this we would create some duplicate code. The definition of the back button and the finish button would look the same in each controller class. Therefore we will create an abstract class that contains these definitions and all other view controllers will depend on it:

public class AbstractWizardController {

    @FXML
    @BackAction
    private Button backButton;

    @FXML
    @LinkAction(WizardDoneController.class)
    private Button finishButton;

    public Button getBackButton() {
        return backButton;
    }

    public Button getFinishButton() {
        return finishButton;
    }
}

Now a controller for the wizard will look like this:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller extends AbstractWizardController {

    @FXML
    @LinkAction(Wizard2Controller.class)
    private Button nextButton;
}

Note: The injection of private nodes in super classes is a feature of DataFX. So if you will try this by using the default FXMLLoader of JavaFX this won’t work. In addition the FXMLLoader doesn’t support the injection of FXML nodes that are defined in a sub-fxml that is included by using the fx:include tag. As a limitation this nodes must not have a CSS id defined because this will override the fx:id in the java object and in that case DataFX can’t inject them. I plan to open a issue at OpenJFX for this.
As a last step we need to disable the “back” button on the first view and the “next” and “finish” buttons on the last view. This can be done in the view controller by defining a method with the @PostConstruct annotation that will be called once the controller instance is created:

@FXMLController(value="wizardDone.fxml", title = "Wizard: Finish")
public class WizardDoneController extends AbstractWizardController {

    @FXML
    private Button nextButton;

    @PostConstruct
    public void init() {
        nextButton.setDisable(true);
        getFinishButton().setDisable(true);
    }
}

Once this is done the wizard is completed an can be displayed in a JavaFX application. Therefore we define the following main class:

public class Tutorial3Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(WizardStartController.class).startInStage(primaryStage);
    }
}

The complete source of this tutorial can be found here.
Here is an movie of the finished wizard application:

DataFX 8 Tutorial 2

As I mentioned here I started a series of DataFX tutorials. The first tutorial can be found here.

In this second tutorial I will show you the basics about navigation in a DataFX flow. In this example we will define two different views that are part of a flow and then navigate from one view to the other one. The source of the tutorial can be found here.

The following pictures shows the two views of the tutorial and its interaction:
tutorial2
As shown in the first tutorial we want to start by defining the views in FXML. Here is the FXML file for the first view:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
    <children>
        <VBox alignment="CENTER" prefHeight="-1.0" prefWidth="300.0" spacing="12.0">
            <children>
                <Label fx:id="resultLabel" text="This is view 1" />
                <Button fx:id="actionButton" mnemonicParsing="false" text="Navigate to view 2" />
            </children>
            <padding>
                <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
            </padding>
        </VBox>
    </children>
    <padding>
        <Insets />
    </padding>
</StackPane>

This defines a view that looks like this:
pic1
The second view differ only in a few points from the first one. Here is the FXML definition of the second view:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
  <children>
    <VBox alignment="CENTER" prefHeight="-1.0" prefWidth="300.0" spacing="12.0" style="-fx-background-color: red;">
      <children>
        <Label fx:id="resultLabel" text="This is view 2" />
        <Button fx:id="actionButton" mnemonicParsing="false" text="Navigate to view 1" />
      </children>
      <padding>
        <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
      </padding>
    </VBox>
  </children>
  <padding>
    <Insets />
  </padding>
</StackPane>

As you can see in the code there is a style addition in the VBox tag. Here the red background color of the view is defined by CSS. The rendered view will look like this:
pic2
As a next step we create view controllers for the views. As shown in the last tutorial you have to define a class for each view that acts as its controller. In a first step we create some empty controller classes that are only annotated by @FXMLController:

@FXMLController("view1.fxml")
public class View1Controller {

    @FXML
    private Button actionButton;
}

Only the button that is defined by the fx:id attribute in the FXML file is part of the first controller class. The second one will look similar:

@FXMLController("view2.fxml")
public class View2Controller {

    @FXML
    private Button actionButton;
}

Once this is done an application that is based on a flow can be created. As learned in tutorial 1 there is an easy way to create an application that only contains one flow. We will use it here, too. The main class of this example will look like this:

public class Tutorial2Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(View1Controller.class).startInStage(primaryStage);
    }
}

By doing so a new flow is created that defines view1 as its start view. If you start the application you will already see the first view but the button won’t trigger any action. To add an action that links to the second view DataFX provides a special annotation called @LinkAction. This annotation is working like the @ActionTrigger annotation that was introduced in tutorial 1. Once the button is clicked an action event will be fired and handled by the DataFX container. When using the @LinkAction annotation the controller of the target view must be specified. Therefore the Annotations provides a value that can be defined as the controller class. Here is the updated code of the first controller class:

@FXMLController("view1.fxml")
public class View1Controller {

    @FXML
    @LinkAction(View2Controller.class)
    private Button actionButton;
}

Whenever the button is pressed the flow will navigate to the second view that is defined by the View2Controller class. The @LinkAction can be added to any JavaFX Node. If the component extends the ButtonBase class or the MenuItem class a handler for action events will be added to the control. Otherwise the action will be called once the control is clicked by mouse. By using the annotation a developer doesn’t need to handle the complete navigation like changing the view or create a new data model. DataFX will handle all these steps automatically and the defined view will appear on screen once the action is triggered.

Maybe you can already imagine how the final code for the second view controller will look like ;)

@FXMLController("view2.fxml")
public class View2Controller {

    @FXML
    @LinkAction(View1Controller.class)
    private Button actionButton;
}

Once this is done you have created the first simply flow with DataFX. By using the @LinkAction annotation you can create big flows that contain a lot different views. In later tutorials you will that DataFX provides some other cool features to manage the navigation in a flow.

As said in the first example each action in DataFX is defined by an unique ID. In the case of a LinkAction, the developer doesn’t need to define an ID on its own. DataFX will create a unique ID once the controller will be initialized. By doing so the source code is much shorter and cleaner. As you will see later in the tutorials there are several other ways to define a navigation in a flow. Some of these work with an unique ID as shown in tutorial 1.

DataFX 8 Tutorial 1

As mentioned here I started a series of DataFX tutorials. In this post I want to show you the first simple tutorial.

In this tutorial we want to create a simple view with only one point of interaction. The source of the tutorial can be found here. The following figure shows how the example will look like:
exasmple1
Every time the button in the view is clicked the text in the top label should change. To create this application we will start with the layout of the view. To do so we need a FXML file that defines the JavaFX view of the application. This can easily be created with Scene Builder. The FXML file (simpleView.fxml) of the tutorial will look like this:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
  <children>
    <VBox alignment="CENTER" prefHeight="-1.0" prefWidth="300.0" spacing="12.0">
      <children>
        <Label fx:id="resultLabel" text="Label" />
        <Button fx:id="actionButton" mnemonicParsing="false" text="Click me!" />
      </children>
    </VBox>
  </children>
  <padding>
    <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
  </padding>
</StackPane>

Once this is created we need a controller class for the view. A first simple version that only uses JavaFX basic API will look like this:

public class SimpleController {

    @FXML
    private Label resultLabel;

    @FXML
    private Button actionButton;
}

Like it is defined in the JavaFX basic API the two fields “resultLabel” and “actionButton” are named as the components defined in the FXML file by its “fx:id” attribute. BY adding the @FXML annotation to these fields they will be automatically injected once the controller will be instantiated.

Until now only JavaFX basic APIs are used. Now we want to add some DataFX magic. As mentioned we need some interaction every time the button is pressed. Because of that we want to trigger an action by the button. DataFX provides two annotations to handle this approach: @ActionTrigger and @ActionMethod.
The @ActionTrigger annotation can be added to a field of the controller that defines a Node in the view. The DataFX container will automatically add an action handler to the Node. Any method that should be called once an action is triggered must be annotated with the @ActionMethod annotation. For both Annotations you must specify a value that defines the unique action id of the action that is handled by the annotation. In this case I used the id “myAction” and added the annotations to the class:

public class SimpleController {

    @FXML
    private Label resultLabel;

    @FXML
    @ActionTrigger("myAction")
    private Button actionButton;

    @ActionMethod("myAction")
    public void onAction() {
        // DO some action...
    }
}

So whenever the “actionButton” will be pressed the onAction() method will be called because they are bound by the same id. The id must be unique in view controller. As you will see in following tutorials there are other types of actions than simply calling a method. For now the most important point is that a component that is annotated with @ActionTrigger can trigger a method that is annotated with @ActionMethod if both annotations define the same unique action id.

As a next step we will implement the action and define a default text for the label:

public class SimpleController {

    @FXML
    private Label resultLabel;

    @FXML
    @ActionTrigger("myAction")
    private Button actionButton;

    private int clickCount = 0;

    @PostConstruct
    public void init() {
        resultLabel.setText("Button was clicked " + clickCount + " times");
    }

    @ActionMethod("myAction")
    public void onAction() {
        clickCount++;
        resultLabel.setText("Button was clicked " + clickCount + " times");
    }
}

as you can see in the code a new method called init() was added. This method should be called after the controller has been initialized and the fields that are annotated with @FXML are injected. In DataFX this can be done by adding the @PostConstruct Annotation to the method. By doing so the DataFX flow container will call this method once all injectable values of the controller instance are injected. There three different types of values / fields that can be injected:

  • UI components that are annotated by @FXML
  • DataFX objects. Here DataFX provides several annotations
  • Custom implementations. These will be injected by using the @Inject annotation

The method that is annotated by @PostContruct will be called when all injections are finished. In this first example we will only use @FXML to inject FXML UI components to the controller.

One thing that is still missing is the binding between the controller class and the FXML file. DataFX provides the @FXMLController annotation for this purpose. By using this annotation the controller class defines its FXML file that contains the layout of the view. After adding the annotation the final controller class will look like this:

@FXMLController("simpleView.fxml")
public class SimpleController {

    @FXML
    private Label resultLabel;

    @FXML
    @ActionTrigger("myAction")
    private Button actionButton;

    private int clickCount = 0;

    @PostConstruct
    public void init() {
        resultLabel.setText("Button was clicked " + clickCount + " times");
    }

    @ActionMethod("myAction")
    public void onAction() {
        clickCount++;
        resultLabel.setText("Button was clicked " + clickCount + " times");
    }
}

Once this is done we need a main class to start the application and show the view on screen. We can’t use the basic FXMLLoader class here because we used some annotations that must be handled by the DataFX container. But since the last preview of DataFX 8.0 the Flow API provides a very simple way to show the view on screen. Here is the complete main class that will start the application:

public class Tutorial1Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(SimpleController.class).startInStage(primaryStage);
    }
}

The shown way is the most simple way to start a flow: the controller class of the start view is always passed as parameter to the constructor of the Flow class. Because in this demo we have only one view we simply pass the SimpleController class to the flow. The Flow class provides a utility method called startInStage() that renders the Flow in a Stage. By doing so the Scene will be created automatically and the Stage will contain a Scene that only contains the defined flow. In this first tutorial the flow will only contain one view.

This first example is quite easy and normally you could define the action binding by only one line of Java code in the init() method:

actionButton.setOnAction((e) -> onAction());

So why are all these annotations used here? As you will see in further tutorials that are more complex than this one it will make sense to use the annotations to provide more readable code.

I hope you liked this first tutorial. I plan to add the second one at the end of the week.