JavaFX : Widgets pour applications professionnelles
Par David MARTIN le lundi 2 février 2009, 14:05 - Java - Lien permanent
Le petit dernier des candidats aux applications RIA, JavaFX, est sorti dépourvu des composants graphiques utiles et nécessaires au développement d'applications à destination des professionnels. Comme Sun semble occupé à mettre le paquet sur une prochaine release en début d'année, incorporant la partie "Mobile", il est peu probable d'en voir arriver tout de suite par le canal officiel.
Alors, quels sont les choix ? Et bien, on se retrouve un peu comme aux débuts de GWT : nous avons un socle technique, il ne reste qu'à retrousser ses manches. Oui mais voilà, tout le monde ne souhaite pas réinventer la roue.
Alors inspirons nous de ce qui a été largement fait pour GWT (par exemple) : le wrapping. Avec GWT il est relativement aisé de wrapper les librairies JavaScript (Ext, SmartClient...) pour pouvoir utiliser les composants existants et souvent de qualité. Avec JavaFX, il est permis de wrapper n'importe quel composant Swing étendant javax.swing.JComponent, autrement dit un nombre assez conséquent :-)
Certes la tâche peut paraitre conséquente - et elle l'est d'ailleurs - mais
si une communauté prend autour de JavaFX, des librairies de ce type peuvent
rapidement apparaître. Voici un tutorial illustrant comment wrapper la célèbre
librairie JFreeChart, tant utilisée par ailleurs !
I- SwingComponent et CustomNode : la base de travail
JavaFX inclut une classe, SwingComponent, permettant d'établir un lien entre le monde Swing et le nouveau monde JavaFX. Cette classe, dont la documentation est disponible à cette URL http://java.sun.com/javafx/1/docs/api/javafx.ext.swing/javafx.ext.swing.SwingComponent.html, propose parmi ses méthodes, la méthode wrap() admettant en unique paramètre un objet de type javax.swing.JComponent. Comme son nom l'indique, cette méthode va encapsuler le composant Swing et retourner un SwingComponent, lui même héritant de javafx.scene.Node. Ce composant pourra ensuite être intégré à une composition graphique classique de JavaFX. Le principe est donc très simple, la mise en oeuvre aussi.
Attardons nous un peu sur CustomNode. Cette classe est dédiée à être étendue pour servir de base à des composants qui seront constitués d'autres composants. A ce titre, parce que SwingComponent va retourner un composant de type Node, le CustomNode sera un parfait wrapper pour accueillir celui-ci et y ajouter des fonctionnalités.
La base de départ sera donc ainsi constituée :
Une classe étendant CustomNode surchargeant sa méthode create(), laquelle retournera un objet héritant de Node, en l'occurence le composant issu de Swing wrappé par SwingComponent.
Voici un exemple de code mettant en oeuvre cette approche :
[...]
public class ChartParameters {
public var properties : Boolean = true;
public var save : Boolean = true;
public var print : Boolean = true;
public var zoom : Boolean = true;
public var tooltips : Boolean = true;
public var legend : Boolean = true;
public var urls : Boolean = true;
}
[...]
public class MyFirstSoGreatChartFX extends CustomNode {
protected var swingComponent : SwingComponent;
protected var dataset : PieDataset;
public var properties : Boolean = true;
public var save : Boolean = true;
public var print : Boolean = true;
public var zoom : Boolean = true;
public var tooltips : Boolean = true;
public var legend : Boolean = true;
public var urls : Boolean = true;
public var dimension : Dimension;
[...]
protected override function getDataset() {
if (dataset == null) {
dataset = new DefaultPieDataset();
}
return dataset;
}
public override function create() : Node {
var chart = ChartFactory.createPieChart3D(
"title",
getDataset() as PieDataset,
"legend",
"tooltips",
"urls"
) as Chart;
var chartPanel = new ChartPanel(chart,
this.dimension.width,
this.dimension.height,
this.dimension.width,
this.dimension.height,
this.dimension.width,
this.dimension.height,
true, //boolean useBuffer
properties,
save,
print,
zoom,
tooltips,
) as ChartPanel;
swingComponent = SwingComponent.wrap(chartPanel);
return swingComponent;
}
}
Ce composant peut ensuite être manipulé comme tout autre dans JavaFX.
Exemple :
[...]
var pieDataSetTest : DefaultPieDataset = new DefaultPieDataset();
pieDataSetTest.setValue("val1", new Double(10));
pieDataSetTest.setValue("val2", new Double(10));
pieDataSetTest.setValue("val3", new Double(5));
pieDataSetTest.setValue("val4", new Double(30));
pieDataSetTest.setValue("val5", new Double(20));
Stage {
title: "When charts come to JavaFX"
width: 800
height: 800
scene: Scene {
content: Group {
content: [
Text {
content: "When charts come to JavaFX"
font: Font {
size: 24
}
},
MyFirstSoGreatChartFX {
translateY: 20
dataset: bind pieDataSetTest;
title: "Super Pie Chart"
dimension: bind new Dimension(400, 300)
backgroundPaint: Color.WHITE
}
]
}
}
}
Nous obtenons avec cet exemple une mise en oeuvre très simple d'un PieChart. Il ne reste ensuite qu'à étendre la librairie avec les autres types de charts.
II- Le binding : pratique et efficace
Parmi les caractéristiques fortes de JavaFX figure celle du binding, permettant ce qui a fait défaut à Swing et que d'autres frameworks de présentation proposent, cad établir un lien automatique entre un objet représentant le modèle et l'objet graphique, représentant la vue. Ainsi la synchronisation des données entre les deux couches se fait de façon transparente, en mode unidirectionnel ou bidirectionnel selon la configuration.
L'exemple du point précédent montre la mise enoeuvre du binding entre le composant graphique MyFirstSoGreatChartFX et le Dataset (ainsi que la Dimension). Ce type de binding va permettre de modifier les données du dataset et d'obtenir une mise à jour du PieChart synchronisée sans rien avoir à ajouter d'autre que la déclaration "bind". Simple et efficace, donc élégant :-)
III- L'approche native
Une approche plus pérenne serait de ne pas avoir à wrapper une librairie existante mais plutôt de disposer de composants natifs riches, pouvant être étendus par la suite pour coller à des besoins plus fins. Bien sûr JavaFX propose des composants pouvant être étendus mais ils sont d'assez bas niveau fonctionnel et demandent un effort assez conséquent pour obtenir des composants de haut niveau. On peut espérer que la librairie par défaut s'étoffe en ce sens et permette pourquoi pas, aux équipes développant les libraries actuelles de les porter en composants natifs pour JavaFX.
IV- Conclusion
JavaFX offre les facilités pour construire une librairie de composant avancés, ces composants qu'on aurait voulu voir livrés de série. L'effort pour réaliser une librairie est bien sûr non neutre mais pas insurmontable non plus. Tout dépend de l'intérêt que la communauté portera à ce petit dernier des frameworks pour RIA. Si cela accroche, il est fort probable que le panel des composants connaisse un essort rapide. Il est aussi à noter que le wrapping n'est utile que pour les applications JavaFX pour desktop. Le profil mobile ne supportant pas le wrapping de composant Swing. Créateurs d'applis mobiles, passez votre chemin pour le moment :-(
En attendant, vous pouvez retrouver quelques composants que j'ai commencé à réaliser ici :
http://code.google.com/p/javafxwidgetlib/
ainsi qu'une démo simpliste :
http://javafxwidgetlib.googlecode.com/svn/trunk/JavaFXWidgetLib-demo/JavaFX-Widgets-Library.html
Toute participation est la bienvenue :-)
