Affichage des articles dont le libellé est JavaFX. Afficher tous les articles
Affichage des articles dont le libellé est JavaFX. Afficher tous les articles

mardi 2 juin 2015

JavaFX : quelques outils d'aide / support au développement

Dans l'éco-système Java, il y a les outils de développement mais aussi des outils complémentaires très utiles pour les développeurs.

JIRA est un système de suivi de bugs, un système de gestion des incidents et un système de gestion de projets développé par Atlassian Software Systems.

Par exemple vous trouverez les bugs et incidents liés aux versions actuelles du JDK JavaFX.

Encore plus intéressant, vous pouvez déclarer un incident - incident pris en charge par un membre de l'équipe JDK.

L'incident recevra un niveau de priorité.

Une fois l'incident déclaré, vous pouvez suivre le traitement de cet incident et connaître la version future du JDK qui fixera le problème (si toutefois votre problème peut être résolu).

Soyez précis lors de la déclaration d'un incident : le membre de l'équipe JDK doit pouvoir reproduire le problème facilement.

Etant donné que le JDK JavaFX est toujours en version EA, de nombreux bugs sont encore en cours.

Cet outil est donc utile et vous servira de "dashboard" global sur le taux de traitement des problèmes liés au JDK.

OpenJDK est une espace collaboratif à la fois pour l'implémentation Open Source de Java et pour l'implémentation de projets autour de la plateforme Java.

Le JDK JavaFX est issu de projets gérés sur cet espace.

Très intéressant : vous avez la ROADMAP des versions actuelles et futures du JDK - information non négligeable à prendre en considération dans votre planning de développement.

La difficulté d'un projet JavaFX est de pouvoir assoir ce projet sur une version stable du JDK JavaFX.

D'autre part la version choisie doit correspondre aux besoins de votre projet.

Nous sommes aussi dans une transition entre le JDK 8 et le prochain JDK 9 : les API sont (et vont être) modifiées et améliorées.

Quelle version faut-il prendre en compte pour notre projet ?

Autant de questions qu'il faut se poser rapidement.

JavaFx : Runtime / Jar

Pour tester votre application, vous exécutez (par exemple sous Eclipse) votre application (en mode Runtime).

Les tests sont positifs, donc tout va bien.

Vous packagez (génération du fichier Jar) votre application avec succès.

Quelque fois dans l'euphorie ou la précipitation un test est oublié :  le test après Packaging de l'application.

Nous partons souvent du principe que puisque les tests sont corrects en mode Runtime forcément ils seront corrects en mode Packaging !

Si cela est le cas, nous faisons une grosse erreur : l'application doit être testée dans les deux modes.

Cette erreur peut amener son lot de problèmes.


Prenons un exemple concret pour illustrer.

Dans toute application, il y a très souvent des accès à des ressources (par exemple : lire un fichier XML ou lire un fichier IMAGE).

Selon le contexte (et les fonctions utilisées) certaines ressources sont accessibles d'une certaine façon en mode Runtime et d'une autre en mode Packaging.

Exemple :

En mode Runtime

public static final String FOP_CONFIGURATION_FILE = "mycfg.xml";

String config = MyClasse.class
.getResource(FOP_CONFIGURATION_FILE).toString()
.substring(6); (*)

fopFactory.setUserConfig(new File(config));

(*) un <substring(6)> est nécessaire car nous obtenons : file:/D:/..../mycfg.xml (chemin absolu dans le file system) : il faut donc supprimer le préfixe "file:/"

Dans cet exemple nous accédons à un fichier de configuration nécessaire pour initialiser le moteur "XSL:FO" (transformer des données XML - par exemple générer un fichier PDF à partir de ces données).

Le fichier FOP_CONFIGURATION_FILE se trouve dans le même package Java que la classe MyClasse.

En mode Runtime, le même package = le même répertoire.

En mode Packaging (lors de l'exécution du Jar)

fopFactory.setUserConfig(this.getClass()
.getResource(FOP_CONFIGURATION_FILE).toURI().toString());

Dans ce cas il faut transformer le chemin de notre fichier en URI pour initialiser notre moteur XSL.


Nous avons donc deux approches très proches mais différentes pour accèder à une même ressource.

Quelle est donc la bonne pratique pour être sûr que le code de notre exemple s'exécute correctement dans les deux modes d'exécution ?

if (MyClasse.class.getResource(FOP_CONFIGURATION_FILE)
.toString().contains("jar:")) {
        // accès en mode Packaging
fopFactory.setUserConfig(this.getClass()
.getResource(FOP_CONFIGURATION_FILE).toURI().toString());

} else {
// accès en mode Runtime
String config = MyClasse.class
.getResource(FOP_CONFIGURATION_FILE).toString()
.substring(6);

fopFactory.setUserConfig(new File(config));
}

Si la valeur retournée par la fonction <getResource> contient "jar:" alors nous sommes en mode Packaging sinon nous sommes en mode Runtime.

Vous avez notez qu'une ressource qui se trouve dans le Jar n'est pas un fichier au sens strict : c'est une "composante" d'un fichier Jar.


Cet exemple a servi à illustrer l'importance de tester tous les comportements (notamment les accès aux ressources) de votre application qu'elle soit en mode DEV ou en mode PRODUCTION.

Il faudra peut être isoler les cas qui vont nécessiter un traitement particulier en mode Runtime ou en mode Packaging.

jeudi 7 mai 2015

JavaFX / IHM : programmative ou déclarative ?

En JavaFX, nous avons la possibilité de définir une IHM de deux façons à la fois différentes et complémentaires :

  • En exploitant des fichiers FXML (façon déclarative)
  • En "assemblant" dynamiquement tous les composants UI nécessaires (façon programmative)

Les fichiers FXML fournissent les informations sur les composants UI qui vont être utiliser.

Ces fichiers vont être considérés par JavaFX comme des objets sérialisés parfaitement utilisables par votre application.

JavaFX Scene Builder est une application WYSIWIG qui permet de générer ces fichiers FXML.

Vous pouvez bien sûr générer manuellement ces fichiers.

La façon programmative va s'appuyer strictement sur les API Java pour construire une IHM.

Certains développeurs adopteront (par confort, par habitude ou par "philosophie") une des deux approches.

Quelle est donc la meilleure approche : programmative ou déclarative ?

En fait il n'y a pas de meilleure approche.

Au début de cet article, nous avons considéré ces deux approches comme complémentaires.

Et c'est le cas !

JavaFX permet de construire une IHM en utilisant ces deux approches.

Nous pouvons par exemple construire une partie de notre IHM avec l'approche déclarative et construire l'autre partie avec l'approche programmative.

Une réflexion au préalable est souhaitable pour estimer la part du déclaratif / programmatif.

"L'estimation" sera peut être conditionnée par certaines contraintes (par exemple : volonté de ne pas externaliser une IHM).

Nous avons un palette d'outils : à nous de les utiliser au mieux !

mercredi 21 janvier 2015

JavaFX / ANT / MSI : le trio gagnant !

Pour pouvoir générer un fichier MSI (Windows Installer), il faut d'abord installer Wix Toolset.

Wix est un ensemble d'outils qui permettent de créer le <Windows Installer> de votre application (.MSI) à partir de documents XML.

Ces documents XML (.WXS, .WXL par exemple) vont "décrire" l'installation de votre application.

Une fois Wix installé, intéressons - nous au fichier <BUILD.XML> de notre application JavaFX.

Pour rappel, ce fichier va "builder" votre application et générer dans notre cas le fichier MSI.

L'exécution de ce fichier se fait par ANT.

Voici un extrait d'un fichier <BUILD.XML> type :

<?xml version="1.0" encoding="UTF-8"?>
<project name="mon application" default="do-deploy" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">
<property name="classpath" location="chemin JDK\jre\lib\ext\jfxrt.jar;" />

<target name="init-fx-tasks">
<path id="fxant">
<filelist>
<file name="${basedir}" />
</filelist>
</path>

<taskdef resource="com/sun/javafx/tools/ant/antlib.xml" uri="javafx:com.sun.javafx.tools.ant" classpathref="fxant" classpath="chemin JDK\lib\ant-javafx.jar" />
</target>

<target name="setup-staging-area">
<delete dir="externalLibs" />
<delete dir="project" />
<delete dir="projectRefs" />

<mkdir dir="externalLibs" />

<copy todir="externalLibs">
<fileset dir="chemin mon projet\lib">
<include name="*" />
</fileset>
</copy>

<mkdir dir="project" />

<copy todir="project">
<fileset dir="chemin mon projet">
<include name="src/**" />
</fileset>
</copy>

<mkdir dir="projectRefs" />

</target>

<target name='do-compile'>
<delete dir="build" />
<mkdir dir="build/src" />
<mkdir dir="build/lib" />
<mkdir dir="build/classes" />

<copy todir="build/lib">
<fileset dir="externalLibs">
</fileset>
</copy>

<copy todir="build/src">
<fileset dir="project/src">
<include name="**/*" />
</fileset>
</copy>

<javac includeantruntime="false" source="1.8" target="1.8" srcdir="build/src" destdir="build/classes" encoding="Cp1252">
<classpath>
<fileset dir="build/lib">
<include name="*" />
</fileset>
</classpath>
</javac>

<copy todir="build/classes">
<fileset dir="project/src">
<exclude name="**/*.java" />
</fileset>
</copy>
</target>

<target name="do-deploy" depends="setup-staging-area, do-compile, init-fx-tasks">
<delete file="dist" />
<delete file="deploy" />

<mkdir dir="dist" />
<mkdir dir="dist/lib" />

<copy todir="dist/lib">
<fileset dir="externalLibs">
<include name="*" />
</fileset>
</copy>

<mkdir dir="dist/resources" />

<copy todir="dist/resources">
<fileset dir="../resources" />
</copy>

<mkdir dir="package" />

<fx:resources id="appRes">
<fx:fileset dir="dist" includes="mon_application.jar" />
<fx:fileset dir="dist" includes="lib/*" />
<fx:fileset dir="dist" includes="resources/**" />
</fx:resources>

<fx:application id="fxApplication" name="MonApplication" mainClass="application.Main" version="x.x.x" />

<mkdir dir="build/classes/META-INF" />

<fx:jar destfile="dist/mon_application.jar">
<fx:application refid="fxApplication" />
<fileset dir="build/classes" />
<fx:resources refid="appRes" />

<manifest>
<attribute name="Implementation-Vendor" value="Mon Entreprise" />
<attribute name="Implementation-Title" value="MonApplication" />
<attribute name="Implementation-Version" value="x.x.x" />
<attribute name="JavaFX-Feature-Proxy" value="None" />
</manifest>
</fx:jar>

<mkdir dir="deploy" />

<fx:deploy verbose="true" embedJNLP="false" extension="false" includeDT="false" offlineAllowed="true" outdir="${basedir}/deploy" outfile="mon_application" nativeBundles="msi" updatemode="background">
<fx:info title="mon application" vendor="mon entreprise" />
<fx:application refId="fxApplication" />

<fx:resources refid="appRes" />

<fx:info vendor="mon entreprise" description="deploiement de mon application" />

</fx:deploy>

</target>*
</project>

Nous ne rentrerons pas dans le détail de ce fichier mais vous noterez que la balise <fx:deploy> permet de spécifier le type de "bundle" à savoir dans notre cas le type "MSI".

Exécutez ce fichier <BUILD.XML> (après quelques customisations bien sûr) : votre fichier MSI est généré !

dimanche 4 janvier 2015

Comment customiser un composant TabPane en JavaFX ?

Le CSS associé avec JavaFX permet de customiser les composants UI.

Intéressons - nous au composant TabPane qui permet la gestion des onglets.

Exemple

.tab-pane *.tab-header-background {
    -fx-background-color: -fx-outer-border, -fx-inner-border, derive(-fx-color, 40.0%);
    -fx-effect: innershadow(two-pass-box , rgba(0.0,0.0,0.0,0.6) , 4.0, 0.0 , 0.0 , 0.0);
}

Dans cet exemple, la couleur de fond du composant TabPane (containeur des onglets) est "déduite" d'un effet qui va être appliqué.


Le label de chaque onglet peut être aussi customisé.

Exemple

.tab-label {
-fx-font-size: 9.0pt;
}

Dans cet exemple la taille de la police est fixée à 9 points.


Nous pouvons customiser le style d'un onglet lors de sa sélection.

Exemple

.tab:selected {
-fx-text-fill: #0026FF;
}

Dans cet exemple nous "ajoutons" une couleur au texte de l'onglet sélectionné.

Customiser une TableView en JavaFX

Comment customiser une TableView en JavaFX ?

Il est possible de customiser le style d'un composant UI avec l'aide des propriétés de CSS.

Pour rappel, le CSS pour JavaFX diffère quelque peu du CSS pour le HTML.

Outre le préfixe "-fx-", certaines propriétés CSS du HTML ne sont pas supportées par le CSS de JavaFX.

Commençons par quelques propriétés générales.

Exemple

.tableView {
-fx-font-size: 8.0pt;
-fx-background-color: linear-gradient(white, #EDEFFF); 
}

Dans cet exemple la taille de la police est fixée à 8 points et la couleur de fond est une couleur créée à partir d'un effet de type <gradient>.

Il est possible de "cacher" le scrolling (horizontal ou vertical) - pratique car sinon les éléments du scrolling apparaîssent en grisé.

Exemple

.table-view *.scroll-bar:horizontal *.increment-button,
.table-view *.scroll-bar:horizontal *.decrement-button {
    -fx-background-color: null;
    -fx-background-radius: 0.0;
    -fx-background-insets: 0.0;
    -fx-padding: 0.0;
}

.table-view *.scroll-bar:horizontal *.increment-arrow, 
.table-view *.scroll-bar:horizontal *.decrement-arrow {
    -fx-background-color: null;
    -fx-background-radius: 0.0;
    -fx-background-insets: 0.0;
    -fx-padding: 0.0;
    -fx-shape: null;
}

Nous pouvons customiser la sélection d'une ligne ou le passage sur une ligne de la TableView.

Exemple

.table-view:row-selection .table-row-cell:filled:hover {
    -fx-background-color: #FFE3C1;
    -fx-background-insets: 0.0, 0.0 0.0 0.0 0.0;
    -fx-text-fill: -fx-text-inner-color;
}

.table-view:focused .table-row-cell:filled:selected {
    -fx-background-color: #BCD4E5;
    -fx-background-insets: 0.0, 0.0 0.0 0.0 0.0;
    -fx-text-fill: -fx-selection-bar-text;
    -fx-table-cell-border-color: #E0F2FF;
}

Les titres des colonnes de notre TableView peuvent être aussi customisés.

Exemple

.table-view .column-header {
-fx-font-weight: bold;
  -fx-background-color: #FFF5EF;
}

.table-view .column-header .text {
  -fx-underline: true;
}

A noter dans cet exemple que le texte du titre de chaque colonne sera souligné.

Les colonnes peuvent être customisées.

Exemple

.table-cell {
    -fx-alignment: BASELINE_CENTER;
 }

Dans cet exemple le contenu de chaque colonne est centré.