I. Un peu d'histoire▲
Avant de commencer l'application, un peu d'histoire sur JavaFX.
Sun a sorti la première release stable de JavaFX en 2008. Son but était de concurrencer les environnements Silverlight de Microsoft et Flex d'Adobe. Le framework est également le remplaçant de Swing, l'ancienne bibliothèque de composants graphiques de Java.
Depuis, Silverlight est mort et Flex a été donné à la fondation Apache en 2011.
De son côté, Oracle continue de faire évoluer JavaFX en lui donnant de nouvelles fonctionnalités et en le déployant sur iOS et Android.
Dans sa première version, JavaFX ne permettait pas de développer en Java — il fallait passer par le langage JavaFX Script. Celui-ci fut abandonné à partir de JavaFX 2. Toutefois, le framework ne faisait toujours pas partie du JDK par défaut, imposant des manipulations supplémentaires pour déployer une application JavaFX.
Ce défaut est maintenant corrigé et JavaFX est entièrement intégré au JDK, ce qui simplifie son déploiement, surtout sur des systèmes embarqués. De plus, depuis cette version, JavaFX a été « opensourcé », ce qui aide grandement au développement.
II. Créer une application JavaFX▲
L'application que je vous propose de créer est simple. Elle se compose d'une fenêtre dans laquelle on saisit un code d'action boursier et une durée. Une fois ces éléments saisis, l'application affiche le tableau des cinq derniers jours de cotation ainsi qu'un graphique représentant l'évolution de l'action sur la durée choisie.
Ce simple exemple va nous permettre d'aborder plusieurs points de JavaFX.
Pour la partie récupération des données, je m'appuie sur le service de Yahoo finance qui permet de récupérer l'historique de valeurs sur une période donnée. Je ne rentrerai pas dans les détails de l'implémentation dans l'article, mais le service est disponible dans le code source.
Commençons par créer la fenêtre principale de l'application. Comme toute application Java, une application JavaFX a besoin d'un point d'entrée. Il s'agit d'une classe étendant la classe javafx.application.Application.
Cette classe définit le cycle de vie d'une application JavaFX :
- au lancement de l'application, la méthode launch() doit être appelée ;
- cette méthode appelle la méthode init() ;
- puis la méthode start() obligatoirement implémentée par le client ;
- une boucle est lancée pour gérer les événements ;
- si un signal d'exit est lancé, la méthode stop() est appelée.
La classe abstraite Application fournit déjà des implémentations pour les méthodes init() et stop() qui ne font rien. Seule la méthode start() doit être implémentée obligatoirement.
Ce qui nous donne un code comme celui-ci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
public
class
Main extends
Application {
public
static
void
main
(
String[] args) {
launch
(
args);
}
@Override
public
void
start
(
Stage primaryStage) {
primaryStage.setWidth
(
1024
);
primaryStage.setHeight
(
968
);
primaryStage.setTitle
(
"JavaFX Xebia"
);
primaryStage.show
(
);
}
}
La méthode static main(), (point d'entré de toute application Java) appelle la méthode launch() d'Application.
Nous implémentons la méthode start() qui reçoit l'objet javafx.stage.Stage qui représente la fenêtre principale de notre application.
La fenêtre est totalement vide, nous allons donc ajouter des éléments à celle-ci.
En JavaFX, il est possible de tout faire en Java pur. Nous pourrions écrire quelque chose comme ça :
2.
3.
4.
5.
Group group =
new
Group
(
);
VBox vbox =
new
VBox
(
);
vbox.getChildren
(
).addAll
(
new
Text
(
"Code Java pur"
));
group.getChildren
(
).add
(
vbox);
primaryStage.setScene
(
new
Scene
(
group));
Ce qui afficherait un texte « Code Java pur » en haut à gauche de la fenêtre.
Créer des composants graphiques à l'aide de code Java est possible (bien qu'ennuyeux), mais peut vite devenir un peu trop verbeux.
Heureusement, il est possible de faire autrement grâce aux fichiers FXML.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import java.lang.*?>
<AnchorPane
prefHeight
=
"968.0"
prefWidth
=
"1024.0"
xmlns
=
"http://javafx.com/javafx/8"
xmlns
:
fx
=
"http://javafx.com/fxml/1"
fx
:
controller
=
"fr.xebia.blog.jfx.Controller"
>
<children>
<VBox>
<children>
<HBox
prefWidth
=
"200.0"
>
<children>
<Label
prefHeight
=
"16.0"
prefWidth
=
"187.0"
text
=
"Code action"
/>
<TextField
fx
:
id
=
"code"
prefWidth
=
"150.0"
/>
</children>
<padding>
<Insets
left
=
"10.0"
top
=
"10.0"
/>
</padding>
</HBox>
<HBox
prefWidth
=
"200.0"
>
<children>
<Label
prefHeight
=
"16.0"
prefWidth
=
"187.0"
text
=
"Période (mois)"
/>
<ChoiceBox
fx
:
id
=
"duration"
prefHeight
=
"26.0"
prefWidth
=
"150.0"
>
</ChoiceBox>
</children>
<padding>
<Insets
left
=
"10.0"
top
=
"10.0"
/>
</padding>
</HBox>
<HBox>
<children>
<Button
mnemonicParsing
=
"false"
onAction
=
"#run"
text
=
"Lancer"
/>
</children>
<padding>
<Insets
left
=
"10.0"
top
=
"10.0"
/>
</padding>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
III. La présentation : FXML▲
Les fichiers FXML sont des fichiers XML qui vont permettre de décrire notre interface de manière un peu plus visuelle que le code brut. De plus, il est possible de binder les éléments FXML directement sur des classes Java. C'est ce que nous allons faire pour créer notre interface.
Il n'y a pas de schéma prédéfini pour un fichier FXML, vous pouvez donc créer n'importe quelle partie de votre interface dans un fichier FXML, et pas obligatoirement une fenêtre entière.
Commençons par créer le formulaire de notre application.
Il est obligatoire dans un fichier FXML d'importer les classes utilisées comme pour du code Java, d'où la présence des balises <?import?>. Le premier élément de notre fichier est un AnchorPane. Les différents types de panes disponibles sont des conteneurs qui permettent d'englober vos éléments dans des panneaux. Il existe plusieurs types de panes en fonction de la disposition que vous souhaitez (fixe, flottant, avec bordure…).
Un fichier FXML est une description de nos objets, on peut donc directement setter tous les attributs que l'on souhaite comme la hauteur de notre AnchorPane.
Remarquez ensuite les Vbox et HBox utilisées régulièrement. Ces « boites » très utiles permettent d'aligner leurs enfants automatiquement de manière verticale (VBox) ou horizontale (HBox). Ainsi les labels et leurs inputs (TextField ou ChoiceBox) sont alignés de manière horizontale sans manipulation particulière.
Comme je l'ai dit plus haut, il est possible de binder notre FXML directement avec nos classes Java. Remarquez sur le AnchorPane l'attribut suivant :
fx:controller="fr.xebia.blog.jfx.Controller"
Il indique à JavaFX que le comportement de notre scène est lié à la classe Controller. Celle-ci sera donc directement instanciée lors de la lecture du fichier FXML.
Regardons cette classe Controller :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public
class
Controller implements
Initializable {
@FXML
private
TextField code;
@FXML
private
ChoiceBox duration;
@Override
public
void
initialize
(
URL url, ResourceBundle resourceBundle) {
}
}
Rien de particulier n'est nécessaire pour indiquer que cette classe est utilisée par JavaFX. Toutefois, il est possible (mais non obligatoire) d'implémenter l'interface javafx.fxml.Initializable, ce qui lancera la méthode initialize() à l'instanciation de la classe si l'on souhaite faire des choses particulières.
Notez l'annotation @FXML, qui permet de binder un objet du fichier FXML avec la classe Java et de pouvoir intervenir dessus. Le binding se fait sur le nom de l'attribut. Par exemple, le TextField « code » sera bindé comme suit grâce à l'attribut fx:id :
<TextField
fx
:
id
=
"code"
prefWidth
=
"150.0"
/>
Même chose pour le menu déroulant « duration ».
Enfin dernier point de binding sur notre FXML, l'attribut onAction sur notre bouton qui bind directement sur la méthode associée du controller.
Cette méthode à la signature suivante :
public
void
run
(
ActionEvent event);
L'event peut nous servir pour retrouver les données associées au clic sur le bouton. Il existe des attributs similaires pour d'autres events comme : onMousePressed ou onKeyPressed.
Si l'écriture de XML à la main est une tâche qui vous rebute, sachez qu'il est possible d'utiliser l'outil SceneBuilder qui propose une interface graphique pour créer nos fichiers FXML. Il est plutôt bien fait : il propose l'ensemble des composants disponibles, crée un code propre et s'intègre très facilement avec IntelliJ, ce qui le rend vraiment utile.
Il est possible de créer ses propres composants FXML afin de les réutiliser plusieurs fois dans une application. Certains frameworks, comme AquaFX proposent d'ailleurs des ensembles de composants évolués.
Le CSS est également de la partie, puisqu'on peut l'utiliser pour styliser notre application, malheureusement c'est un CSSFX, c'est-à-dire avec certaines limitations par rapport au vrai CSS. Enfin, il est possible d'écrire des fonctions JavaScript dans les fichiers FXML. Cela peut servir par exemple pour des traitements simples lors d'un clic sur un bouton.
Le formulaire est prêt, il ne reste plus qu'à l'afficher. Afficher du FXML avec JavaFX ne se fait pas de manière automatique, il faut lui indiquer le fichier à charger.
Nous allons créer une classe JFxUtils qui s'occupe de charger les fichiers :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
public
class
JfxUtils {
public
static
Node loadFxml
(
String fxml) {
FXMLLoader loader =
new
FXMLLoader
(
);
try
{
loader.setLocation
(
JfxUtils.class
.getResource
(
fxml));
Node root =
(
Node) loader.load
(
Main.class
.getResource
(
fxml).openStream
(
));
return
root;
}
catch
(
IOException e) {
throw
new
IllegalStateException
(
"cannot load FXML screen"
, e);
}
}
}
La méthode loadFxml() de cette classe fait deux choses simples :
- création d'une nouvelle instance de FXMLLoader. C'est elle qui s'occupera du binding, etc. ;
- chargement du fichier dont le chemin est passé en paramètre. Le FXMLLoader crée alors un javafx.scene.Node qui sera retourné.
Pour utiliser cette méthode, il suffit de rajouter dans la méthode start() :
primaryStage.setScene
(
new
Scene
((
Parent) JfxUtils.loadFxml
(
"/fr/xebia/blog/fxml/screen.fxml"
), 1024
, 968
));
Nous créons une scène dans notre fenêtre à partir du nœud retourné par la méthode créée précédemment.
Nous avons maintenant un formulaire qui s'affiche à l'écran, mais rien ne se passe lors du clic sur le bouton et le menu déroulant de durée est vide.
IV. La manipulation de liste▲
La manipulation de liste en JavaFX est un peu particulière puisque toutes les collections utilisées à travers le framework doivent être wrappées dans une collection propre à JavaFX tirée de FXCollections. Ce choix a été fait pour permettre de notifier les collections des événements se produisant sur les listes dans l'application. Il est possible d'ajouter des listeners à toutes ces collections dans ce but.
Il existe deux façons de créer des listes en JavaFX :
- directement dans le FXML, si vous connaissez les éléments ;
- par du code Java, pour les éléments dynamiques (il est effectivement impossible de boucler sur une liste directement dans le FXML, un peu comme un ng-repeat en Angular).
Comme nous connaissons le nombre de durées dans notre menu déroulant, nous pouvons passer directement par FXML :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<ChoiceBox
fx
:
id
=
"duration"
prefHeight
=
"26.0"
prefWidth
=
"150.0"
>
<items>
<FXCollections
fx
:
factory
=
"observableArrayList"
>
<Integer
fx
:
value
=
"3"
/>
<Integer
fx
:
value
=
"4"
/>
<Integer
fx
:
value
=
"5"
/>
<Integer
fx
:
value
=
"6"
/>
<Integer
fx
:
value
=
"7"
/>
<Integer
fx
:
value
=
"8"
/>
<Integer
fx
:
value
=
"9"
/>
<Integer
fx
:
value
=
"10"
/>
<Integer
fx
:
value
=
"11"
/>
<Integer
fx
:
value
=
"12"
/>
</FXCollections>
</items>
</ChoiceBox>
JavaFX crée alors une observableArrayList avec les éléments voulus à l'intérieur.
Si vous ne connaissez pas les éléments à l'avance, vous devez passer par le Java. C'est ce que nous allons faire avec notre tableau. Pour rappel, nous voulons afficher les cinq derniers cours de notre action dans un tableau.
Premièrement, ajoutons un tableau dans notre FXML à la suite de notre bouton :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
<HBox
fx
:
id
=
"hboxTable"
prefWidth
=
"200.0"
visible
=
"false"
>
<children>
<TableView
fx
:
id
=
"tableView"
prefHeight
=
"150.0"
>
<columns>
<TableColumn
minWidth
=
"100.0"
prefWidth
=
"75.0"
text
=
"Date"
fx
:
id
=
"columnDate"
>
<cellValueFactory>
<PropertyValueFactory
property
=
"date"
/>
</cellValueFactory>
</TableColumn>
<TableColumn
minWidth
=
"100.0"
prefWidth
=
"75.0"
text
=
"Prix"
>
<cellValueFactory>
<PropertyValueFactory
property
=
"Close"
/>
</cellValueFactory>
</TableColumn>
<TableColumn
minWidth
=
"100.0"
prefWidth
=
"75.0"
text
=
"Volume"
>
<cellValueFactory>
<PropertyValueFactory
property
=
"Volume"
/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</children>
</HBox>
Les tableaux sont représentés par la classe javafx.scene.control.TableView. Dans le FXML, nous allons décrire la structure du tableau. En l'occurrence, trois colonnes : date, prix et volume de transaction.
Nous indiquons aussi un cellValueFactory par colonne. Il y a deux éléments importants dans les TableView :
- la cellValueFactory qui indique à JavaFX quelle valeur afficher et comment l'afficher. Ici nous indiquons que nos valeurs sont des propriétés d'un objet (PropertyValueFactory) et elles seront affichées via un toString() ;
- la cellFactory qui indique comment afficher la cellule. Par défaut du texte, mais on pourrait vouloir afficher un champ texte ou une image.
Notre tableau est maintenant structuré, mais comment le remplir ? Direction le Java.
Dans notre Controller, nous allons binder notre tableau :
2.
@FXML
private TableView<HistoricQuote>
tableView;
Une TableView est liée à un type d'objet, ici un HistoricQuote remonté par le service de Yahoo.
Ensuite dans notre méthode run :
2.
3.
4.
5.
6.
public
void
run
(
ActionEvent event) {
List<
HistoricQuote>
quotes =
yahooService.getHistoric
(
code.getText
(
), (
Integer) duration.getValue
(
));
final
ObservableList<
HistoricQuote>
items =
FXCollections.observableArrayList
(
);
quotes.stream
(
).limit
(
5
l).forEach
(
historic ->
items.add
(
historic));
tableView.setItems
(
items);
}
Nous faisons :
- un appel au service de Yahoo en récupérant les données saisies dans le formulaire ;
- la création de notre FXCollections (avec le même type que la TableView) ;
- l'ajout des cinq éléments voulus ;
- l'ajout de la liste dans le tableau.
Notre tableau est maintenant complété et affiché. Remarquez l'utilisation des lambdas de Java 8 qui simplifient ce code.
Pour terminer sur les tableaux et comme dit plus haut, PropertyValueFactory affiche nos valeurs via un toString(), ce qui, pour notre date, va donner quelque chose comme : « Tue Aug 19 00:00:00 CEST 2014 ».
Heureusement, nous pouvons créer notre propre cellValueFactory pour afficher la date comme on le souhaite.
Commençons par binder notre colonne de date dans notre controller :
@FXML
private TableColumn columnDate;
Dans la méthode initialize() appelée à l'instanciation du controller, nous pouvons faire :
2.
3.
4.
5.
@Override
public
void
initialize
(
URL url, ResourceBundle resourceBundle) {
columnDate.setCellValueFactory
(
value ->
new
SimpleStringProperty
(
new
SimpleDateFormat
(
"dd-MM-yyyy"
).format
(((
HistoricQuote) value.getValue
(
)).getDate
(
))));
}
La value passée en paramètre est de type TableColumn.CellDataFeatures qui nous permet de traiter la valeur de la colonne en cours. Ensuite un simple formatage de date suffit à afficher ce que l'on souhaite.
V. La création de graphiques▲
Dernier élément manquant à notre application : le graphique.
JavaFX propose toute une série de graphiques prêts à l'emploi, ce qui rend leur utilisation relativement simple.
Comme pour tous les éléments JavaFX, il est possible de les créer directement en FXML. Malheureusement, l'axe des Y de notre graphique (les prix) changera dynamiquement, ce qui oblige à recréer entièrement le graphique à chaque fois (une fois les axes fixés, on ne peut plus les changer). Nous allons donc passer en Java.
Côté FXML, un seul ajout, une HBox permettant de binder le tout avec le controller :
<HBox
fx
:
id
=
"hboxGraph"
visible
=
"false"
/>
Côté Java, voici le code complet que je vais expliquer par la suite :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
private
void
fillGraph
(
List<
HistoricQuote>
quotes) {
ObservableList<
XYChart.Series<
String, Float>>
lineChartData =
FXCollections
.observableArrayList
(
);
final
XYChart.Series<
String, Float>
series =
createSerie
(
quotes);
lineChartData.add
(
series);
NumberAxis yAxis =
createYAxis
(
quotes);
final
CategoryAxis xAxis =
new
CategoryAxis
(
);
xAxis.setLabel
(
"Temps"
);
LineChart chart =
new
LineChart
(
xAxis, yAxis, lineChartData);
chart.setPrefWidth
(
1010
);
chart.setPrefHeight
(
400
);
hboxGraph.getChildren
(
).clear
(
);
hboxGraph.getChildren
(
).add
(
chart);
}
private
XYChart.Series<
String, Float>
createSerie
(
List<
HistoricQuote>
quotes) {
SimpleDateFormat dateFormat =
new
SimpleDateFormat
(
"dd-MM-yyyy"
);
final
ObservableList<
XYChart.Data<
String, Float>>
observableList =
FXCollections
.observableArrayList
(
);
quotes.stream
(
).forEach
(
historic ->
{
XYChart.Data<
String, Float>
data =
new
XYChart.Data<
String, Float>(
dateFormat.format
(
historic.getDate
(
)),
historic.getClose
(
));
observableList.add
(
data);
}
);
return
new
XYChart.Series<
String, Float>(
"Evolution du cours"
, observableList);
}
private
NumberAxis createYAxis
(
List<
HistoricQuote>
quotes) {
Optional<
HistoricQuote>
max =
quotes.stream
(
).max
((
h1, h2) ->
{
if
(
h1.getHigh
(
) ==
h2.getHigh
(
))
return
0
;
return
h1.getHigh
(
) <
h2.getHigh
(
) ? -
1
: 1
;
}
);
Optional<
HistoricQuote>
min =
quotes.stream
(
).min
((
h1, h2) ->
{
if
(
h1.getLow
(
) ==
h2.getLow
(
))
return
0
;
return
h1.getHigh
(
) <
h2.getHigh
(
) ? -
1
: 1
;
}
);
return
new
NumberAxis
(
"Variation"
, min.get
(
).getLow
(
), max.get
(
).getHigh
(
), 0.2
);
}
La méthode fillGraph() est appelée dans la méthode run() et prend en paramètre la liste des valeurs retournée par le service de Yahoo.
Voici comment créer le graphique :
- 2 Création d'une FXCollections qui sera la liste des séries de notre graphique. Pour rappel, JavaFX ne travaille qu'avec des listes de ce type ;
- 4 Création d'une série pour le graphique. Il est possible d'ajouter plusieurs séries à notre graphique. Comme nous voulons une courbe, nous prenons une série de points (XY) ;
- 6 Création de l'axe des Y ;
- 7 Création de l'axe des X qui sera une simple suite de String, un point étant égal à une date ;
- 8-10 Création du graphique en lui-même ;
- 11 On clean la HBox créée dans le FXML pour éviter l'empilement de graphiques à chaque run ;
- 12 Ajout du graphique à la HBox, et donc dans la fenêtre.
La série, méthode createSerie(), est créée comme suit :
- 18 Création d'une nouvelle FXCollections. Celle-ci va contenir la liste des points de la courbe ;
- 20-25 Chaque point est créé avec la date en X et les prix de clôture en Y ;
- 26 Création de la série elle-même avec un titre et la liste des points.
Enfin, remarquez la création de l'axe des Y (méthode createYAxis()). C'est lui qui nous oblige à recréer le graphique à chaque clic sur le bouton run.
Cet axe est créé en lui spécifiant le point minimum et maximum du graphique ainsi que l'unité entre deux graduations.
Le prix d'une action étant complètement différent entre deux actions, on ne peut pas fixer un axe des Y identique pour chaque graphique. Malheureusement, il est impossible de changer cet axe de manière dynamique.
VI. La concurrence▲
Le service de Yahoo rend de bons services, mais parfois, il peut mettre du temps à répondre. En l'état actuel des choses, notre application donnera l'impression de planter si le service est lent à répondre.
Pour y remédier, nous allons ajouter une petite fenêtre de chargement le temps que le service nous réponde en utilisant les Task JavaFX. Les Tasks sont une implémentation des Futures et se rapprochent également des promesses JavaScript. Il devient alors simple d'exécuter des traitements asynchrones et d'agir en fonction de la réponse.
Voici la nouvelle implémentation de la méthode run() en y ajoutant notre fenêtre de loading :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
public
void
run
(
ActionEvent event) {
final
Stage progressBar =
openLoadingWindow
(
);
ExecutorService executorService =
Executors.newSingleThreadExecutor
(
);
Task<
YahooResponse>
task =
new
Task<
YahooResponse>(
) {
@Override
protected
YahooResponse call
(
) throws
Exception {
return
yahooService.getHistoric
(
code.getText
(
), (
Integer) duration.getValue
(
));
}
}
;
task.setOnFailed
(
workerStateEvent ->
progressBar.close
(
));
task.setOnSucceeded
(
workerStateEvent ->
{
YahooResponse yahooResponse =
(
YahooResponse) workerStateEvent.getSource
(
).getValue
(
);
progressBar.close
(
);
if
(
yahooResponse !=
null
&&
yahooResponse.getQuery
(
).getCount
(
) >
0
) {
List<
HistoricQuote>
quotes =
yahooResponse.getQuery
(
).getResults
(
).getQuote
(
);
fillTableView
(
quotes);
fillGraph
(
quotes);
}
);
executorService.submit
(
task);
executorService.shutdown
(
);
}
private
Stage openLoadingWindow
(
) {
final
Stage progressBar =
new
Stage
(
);
progressBar.initModality
(
Modality.WINDOW_MODAL);
progressBar.initOwner
(
code.getScene
(
).getWindow
(
));
progressBar.setScene
(
new
Scene
(
new
Group
(
JfxUtils.loadFxml
(
"/fr/xebia/blog/fxml/loading.fxml"
))));
progressBar.show
(
);
return
progressBar;
}
Notre nouvelle fenêtre est un objet Stage comme pour notre fenêtre principale.
Une Task implémente Runnable, nous devons donc créer un objet implémentant la méthode call. C'est dans cette méthode que nous appelons le service Yahoo. Ensuite nous utilisons deux méthodes très utiles des objets de type Task :
- setOnFailed() qui indique le comportement si la tâche échoue. Ici on ferme la fenêtre de loading ;
- setOnSucceeded() qui indique le comportement si la tâche réussit. Ici on ferme la fenêtre de loading et on remplit le tableau et le graphique.
La Task est exécutée dans un ExecutorService basique de Java.
Comme on le voit, il est très simple de faire des traitements asynchrones en JavaFX, ce qui est très pratique pour les longues tâches de calcul ou autres appels de services.
Pour information, voici le FXML de la fenêtre de loading qui utilise un nouvel objet, la ProgressBar :
2.
3.
4.
5.
<AnchorPane
prefHeight
=
"97.0"
prefWidth
=
"291.0"
xmlns
=
"http://javafx.com/javafx/8"
xmlns
:
fx
=
"http://javafx.com/fxml/1"
>
<children>
<ProgressBar
layoutX
=
"46.0"
layoutY
=
"39.0"
prefWidth
=
"200.0"
/>
</children>
</AnchorPane>
VII. Conclusion▲
Notre application est maintenant terminée, voici un exemple d'écran avec l'action Ubisoft :
Vous pouvez retrouver l'intégralité du code source sur le github suivant : https://github.com/RNiveau/article-javafx8
Nous avons vu comment créer des écrans, créer des tâches asynchrones, manipuler le FXML et les listes, soit les tâches les plus courantes dans une application. Mais JavaFX offre bien plus de possibilités dont voici une liste non exhaustive :
- animations d'éléments ;
- lecture Audio/Vidéo ;
- gestion de la 2D/3D ;
- multitouch pour les écrans tactiles ;
- browser web intégré.
Vous pouvez voir une démo de ces fonctionnalités via une application dédiée dans les samples du JDK (http://www.oracle.com/).
Il est donc possible de faire beaucoup de choses avec JavaFX et de manière relativement simple. L'apport du FXML par rapport au tout Java aide beaucoup à la réalisation des interfaces.
Toutefois, il est clair que JavaFX reste un marché de niche. Utiliser JavaFX dans un navigateur est à oublier, le plugin Java des navigateurs n'étant presque plus utilisé au profit des frameworks JavaScript qui permettent de créer facilement des interfaces web complexes.
JavaFX est utilisé là où le besoin d'une application lourde est encore présent (applications en lien avec des drivers spécifiques de machines par exemple) ou sur des systèmes embarqués ne disposant pas forcément de navigateur web.
Le portage de JavaFX sur Android (déjà effectué) pourrait donner un nouveau souffle au framework. Le portage sur iOS également, mais il faudra pour cela qu'Apple accepte le déploiement d'applications contenant une JVM intégrée.
VIII. Remerciements▲
Cet article a été publié avec l'aimable autorisation de la société Xebia qui est un cabinet de conseil Parisien spécialisé dans les technologies Big Data, Cloud, Web, les architectures Java et mobilité dans les environnements agiles.
Nous tenons à remercier Claude Leloup pour sa correction orthographique et Mickael Baron pour la mise au gabarit.