/ DataFX / DataFX 8 Tutorial 1

DataFX 8 Tutorial 1

Hendrik on 2014/05/20 - 09:24 in DataFX, JavaFX

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.

25 POST COMMENT

Send Us A Message Here

Your email address will not be published. Required fields are marked *

25 Comments
  • 2014/05/20

    Very interesting and promising! Such tutorials always make a framework much more appealing to start with.

    tbee
    Reply
  • 2014/05/21

    BUT where the hell is ActionTrigger annotation located in the library? Were you writing this tutorial with one hand, and the other stirring soup?

    gabi
    Reply
  • 2014/05/21

    The tutorial depends on DataFX 8.0b4

    Reply
  • 2014/05/21

    I was so mean with frustration. Thank you, and please excuse my expression.

    gabi
    Reply
  • Pingback:JavaFX links of the week, May 26 // JavaFX News, Demos and Insight // FX Experience

  • 2014/07/14

    Thanks for these interesting tutorials. I’d like to use Eclipse to launch it, is it possible?

    Jade
    Reply
    • 2014/07/14

      Hi,
      all the Tutorials are defined as Maven project. So you should use them in eclipse without a problem.

      Reply
  • 2014/11/05

    Hello, I am following your tutorials and running the examples on Netbeans 8.0.1.
    After changing the pom.xml with the dependencies for core and flow, I am getting this error in Netbeans:
    @FXMLController(“myscene.fxml”)
    incompatible types: FXMLController cannot be converted to Annotation.

    Any help will be appreciated.

    Pablo
    Reply
  • 2014/11/07

    mmh, that sounds weird. Do you use maven or how do you manage your dependencies?

    Reply
  • 2014/11/10

    I fixed the problem. It was my mistake in naming the controller as FXMLController, which is the same name used by DataFX in the @tag. Actually, Netbeans creates a default project with that name. Even though, I can compile the code with success, but it triggers an exception when starting. I put the code in the google group:
    https://groups.google.com/forum/#!topic/datafx-dev/9KfPcoP4Uag
    “Datafx and Netbeans 8.0.1”

    If you can help, it will be appreciated.
    Thx

    Pablo
    Reply
    • 2014/11/10

      Hi,
      I posted an answer with the solution in the google group.

      Reply
  • 2015/02/16

    i have created the same app with eclipse using maven.But the event not firing when clicking the button.
    my pom.xml

    4.0.0
    com.company.product
    DataFX-1
    0.0.1-SNAPSHOT

    io.datafx
    flow
    8.0.7

    jijesh
    Reply
    • 2015/02/16

      Hi,

      use DataFX 8.0.0 instead of 8.0.7 which is a broken artifact.

      Reply
      • 2015/05/24

        So, please delete the version 8.0.7 from the repository because I’ve been frustrated too with that version.

        Randy Cahya Wihandika
        Reply
  • 2015/03/01

    Hi, Thanks for your nice tutorial. I have an issue and the tutorial code does not get compiled and is complaining that method startInStage does not exist in Flow class. I have downloaded the jar files from: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.javafxdata%22
    Please help.

    Mo

    Mo
    Reply
    • 2015/03/01

      Never mind the jar file was corrupted, I downloaded again and it is working fine, thanks.

      Mo

      Mo
      Reply
  • 2015/12/21

    Hi,
    Thank you very much for this tutorial. Where would i found such tutorial for other API’s of datafx like for data sources, web socket and injection, here i think you have covered only the flow API.
    Thank you.

    Nazm
    Reply
    • 2015/12/21

      Hi,
      at the moment we don’t have real tutorials for that API, sorry.

      Reply
      • 2015/12/26

        Thank you very much for your quick response. I tried the first tutorial here. I started with a maven-archetype-quickstart and then added dependency for flow in my pom. Now when i copy paste the code of this tutorial from Bit Bucket then imported controlsfx packages was unrecognizable so i looked into the jar and found it was in io.datafx.controller package and not in org.datafx.controller, so everything worked fine. Is there any thing wrong in using io.datafx.controller.

        Nazm
        Reply
  • 2016/05/31

    Hello!
    I am trying to make same sample app as you, just copying same code, but i get error:
    Caused by: io.datafx.controller.FxmlLoadException: No FXML File specified!
    at io.datafx.controller.ViewFactory.createLoader(ViewFactory.java:226)
    at io.datafx.controller.ViewFactory.createByController(View

    Po
    Reply
  • 2016/06/07

    Error…
    Caused by: io.datafx.controller.FxmlLoadException: Can’t create controller for class class com.oi.datafx.SimpleController

    Caused by: io.datafx.controller.FxmlLoadException: No FXML File specified!

    Flavio Rossi
    Reply