-
Notifications
You must be signed in to change notification settings - Fork 104
Getting Started HelloWorld (english)
This tutorial shows how to implement a simple helloWorld application with mvvmFX.
- Maven
- Oracle JDK version 8 or higher (OpenJDK won't work at the moment because there is no JavaFX included)
- a Java IDE of your choice
- (optional) JavaFX SceneBuilder
In this tutorial we use Maven for handling our build process and managing our dependencies.
At first we create a maven project for the example. Create a file POM.xml
in your project folder. In the POM.xml file we add
the dependency to mvvmFX and change the java version.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mvvmfx-helloworld</artifactId>
<version>1.0.0</version>
<name>HelloWorld Example</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>de.saxsys</groupId>
<artifactId>mvvmfx</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
</project>
In addition we have to create the typical maven folder structure like this:
project_directory
+--src
| +--main
| +--java
| +--resources
+--POM.xml
We start with a java class to launch the application.
In this example we use the package com.example.helloworld
.
We name the class Starter
and add a main method. Additionally we extend from javafx.application.Application
to define the entry point into our JavaFX application.
public class Starter extends Application{
public static void main(String...args){
Application.launch(args);
}
@Override
public void start(Stage stage){
stage.setTitle("Hello World Application");
stage.show();
}
}
This easy example can already be started. An empty window is shown with "Hello World Application" in the title bar.
There are two options to create a user interface with JavaFX: You can create ui elements and put them together in java code like it was done in the old days with Java Swing. The alternative and in most cases the better choice is to use FXML, a XML dialect to describe the JavaFX UI.
One of the reasons to use FXML is the really cool JavaFX SceneBuilder. It is a graphical UI builder which can be used to click FXML files realy fast. It is open-source an can be downloaded here.
MvvmFX supports Views both based on FXML and directly written with Java code. But in this tutorial we will stay with the FXML option.
When FXML is used the conceptual "View" consists of two files:
- The FXML file contains the structure and layout of the UI. It defines what UI controls are used.
- A so called "CodeBehind" class or "View class". This is a Java class that contains references to UI controls from the FXML file. This is needed so that we can access the controls that are defined in the FXML file from Java code.
Both files together are "the View". MvvmFX uses a convention-over-configuration approach with some Naming-Conventions. Simply speaking: Both the FXML file and the CodeBehind class should have the same name (without file extension) and should be located in the same folder/package.
Before we create our View files, we begin with the ViewModel class. In the first place this class will be empty
but we are coming back here in a few minutes. We name this class HelloWorldViewModel
and implement the interface de.saxsys.mvvmfx.ViewModel
.
public class HelloWorldViewModel implements ViewModel {
}
Now we create our view class HelloWorldView
. This class implements the interface
de.saxsys.mvvmfx.FxmlView
. Additionally we need to provide the type of our
ViewModel class as generic type. This way the logical connection between the View
and ViewModel is established. In this example we implement the interface Initializeable
too.
This interface is part of JavaFX and requires you to implement a method initialize
that will be called after the view is completely loaded.
public class HelloWorldView implements FxmlView<HelloWorldViewModel>, Initializable {
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
}
Now we can create our FXML file. Like said before we follow the naming conventions
and name the file HelloWorldView.fxml
and put it into the same directory like
the View class.
Note for Maven: Maven has different directories for Java classes (
src/main/java
) and other resources (src/main/resources
). FXML files need to be placed inside thissrc/main/resources
directory to be handled correctly. But it's still important to satisfy the conventions by putting the file into the same package as the View class. This means that you have to create the same package/directory structure undersrc/main/resources
. In our example we put the FXML file insrc/main/resources/com/example/helloworld/
because our View class has the full qualified namecom.example.helloworld.HelloWorldView
.
Alternatively, we can add the following configuration to our POM.xml file. This way we can put our FXML files in the same folder as our java code:
<project ...> ... <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.fxml</include> </includes> </resource> </resources> ... </build> ... </project>
We could design our FXML file with SceneBuilder or by hand with a XML editor.
At first we add a Label
with the text "HelloWorld" and put it into a VBox
.
It's important that we add a reference to our View class with the fx:controller
attribute.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml" alignment="CENTER"
fx:controller="com.example.helloworld.HelloWorldView">
<children>
<Label text="Hello World" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
To make the view visible we need to load it and put it into the scene graph of
our application. To do this we use the fluent api that is provides by mvvmFX.
The FluentViewLoader
class gets the view class as parameter and returns
a so called ViewTuple
. This is a wrapper class that contains the loaded
View class /CodeBehind, the viewModel and the root element of the FXML file.
@Override
public void start(Stage stage){
stage.setTitle("Hello World Application");
ViewTuple<HelloWorldView, HelloWorldViewModel> viewTuple = FluentViewLoader.fxmlView(HelloWorldView.class).load();
Parent root = viewTuple.getView();
stage.setScene(new Scene(root));
stage.show();
}
At the moment the text message is hardcoded in the FXML file. But the design pattern Model-View-ViewModel says that the state of the View has to be placed in the ViewModel. Let's edit our example to accomplish this.
In the ViewModel we add a StringProperty named "helloMessage" with the initial value "Hello World" together with the getters and setters according to the JavaFX bean conventions.
public class HelloWorldViewModel implements ViewModel {
private StringProperty helloMessage = new SimpleStringProperty("Hello World");
public StringProperty helloMessageProperty(){
return helloMessage;
}
public String getHelloMessage(){
return helloMessage.get();
}
public void setHelloMessage(String message){
helloMessage.set(message);
}
}
The connection between the label in the FXML file and the StringProperty in the ViewModel is done in the View class. To do this we need to add an attribute fx:id to the label in the FXML file.
<Label fx:id="helloLabel"/>
Please note: In fxml there are the attributes "id" and "fx:id" with different meanings. The "id" attribute without the "fx" namespace prefix is mainly used for styling with CSS. For our objective we need the attribute "fx:id". It's possible to use both attributes together without problems. It's probably a good idea to give both attributes the same value to minimize confusion.
In the view class we can now access the label instance. We add an instance variable
of the type Label
and are using the variable name "helloLabel". The name is important:
JavaFX is doing the association of elements in the FXML file and the View class by
matching the "fx:id" attribute value with the names of the instance variables.
Additionally we need to add the annotation javafx.fxml.FXML
to our instance variable.
In the initialize
method of our View class that we have left empty in the first place
we can now connect the label with our ViewModel.
To get an instance of the ViewModel into our View we need add an instance variable of the
ViewModel type (in our case HelloWorldViewModel
) and add the mvvmFX specific annotation
de.saxsys.mvvmfx.InjectViewModel
. Now the framework can do it's work and provide you the
ViewModel instance you need:
public class HelloWorldView implements FxmlView<HelloWorldViewModel>, Initializable {
@FXML
private Label helloLabel;
@InjectViewModel
private HelloWorldViewModel viewModel;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
helloLabel.textProperty().bind(viewModel.helloMessageProperty());
}
}
In the initialize method we use Data-Binding to connect the label and the ViewModel:
The line helloLabel.textProperty().bind(viewModel.helloMessageProperty());
means that
we take the text property of the label and bind it's value to the "helloMessage" StringProperty
of the ViewModel. This way the label will always show the exact value of this
property in the ViewModel. Would we change the value in the ViewModel it would be
automatically and immediately changed in the UI.
The initial value of the property in the ViewModel was "Hello World" so this value
will be visible in the UI at the moment.
You can find the complete code of this example under: helloworld
There is also a variant of this example that doesn't use FXML but the classical java-code way of creating UIs here: helloworld-without-fxml