L’une des nouveautés de Java 7 sera la mise à disposition d’un framework applicatif pour Swing, spécifié dans la JSR-296. Voici un résumé qui vous intéressera si vous souhaitez en savoir plus pour la plateforme Desktop de Java.
Quel est avant tout la justification d’un framework pour swing ?
Swing est disponible depuis presque 10 ans. Il existe un grand nombre d’applications Java aujourd’hui qui ont toutes été écrites avec les moyens du bord. En conséquence (et c’est le cas chez nous) de nombreux développeurs ont implémenté une couche de framework afin de fournir des services de base par dessus Swing. Le constat est que toutes les applications Java qui utilisent Swing se retrouvent à devoir implémenter une partie de plomberie pour fonctionner. C’est l’une des raisons aussi du succès de certaines plateformes comme Eclipse RCP et Netbeans Platform.
Swing a-t-il évolué depuis 5 ans ?
Je mets au défi un développeur Java expérimenté de passer sans erreurs l’un des tests du site Java Black Belt sur Swing. Vous verrez que si vous êtes un maître Jedi en Spring ou EJB3, vous risquez une douche froide en allant faire un tour du côté de Swing. Spécialement depuis Java 5 et Java 6 qui, ne l’oublions pas, ont fait avancer Swing d’un grand pas en avant. La réponse à la question initiale est donc oui.
Avec la JSR-296 et aussi la JSR-295 (Beans binding) nous arrivons à un framework simple et disponible de facto dans Java. Je parlerai de la JSR-295 la semaine prochaine.
Y-a-t-il une comparaison avec Eclipse RCP, Spring RCP ou Netbeans Plaform ?
Le framework de la plateforme Eclipse est un succès car il comble un manque dans l’architecture de base de Java. De même pour Netbeans et son moteur très puissant. En même temps ces frameworks ne sont-ils pas trop gros pour des applications simples ? Et pourquoi devoir se reposer sur une dépendance supplémentaire ?
Le message des leaders de la spécification JSR-296 est : plus simple, moins riche et livré en standard avec Java.
Caractéristiques du framework
Voici maintenant ce qui est couvert par la JSR-296 :
– gestion du cycle de vie d’une application
– chargement de ressources et gestion de l’internationalisation (i18n)
– persistence de la configuration de l’affichage, de la position des fenetres automatiquement
– définition de la notion d’Action au sens cas d’utilisation afin de faciliter l’écriture du code. Utilisation appropriée du moteur de thread de Swing pour effectuer de manière asynchrone certaine tâches.
Gestion du cycle de vie
En premier lieu, une gestion du cycle de vie de l’application. Combien de fois avons-nous eu à développer des shutdown hook afin d’intercepter la fermeture d’une fenêtre ? La JSR-296 propose une class Application qui vous permet de suivre le cycle de vie de votre programme. Parmi les méthodes de cette class nous retrouvons :
– launch() signifie que l’application s’initialise. Vous pouvez afficher un splash-screen par exemple
– startup()
– shutdown()
– exit()
Quoi d’autre ? Un système événementiel de MileStone permet à votre application de signifier à qui veut bien l’écouter (une progress bar) l’avancement du chargement initial.
Enfin un système standard permet de gérer la sortie de l’application, lorsque l’utilisateur clique et que vous souhaitez lui demander s’il veut sauver son fichier par exemple.
Les Ressources
Je ne sais pas si vous avez déjà manipulé des ResourcesBundles en Java. Ceux-ci permettent de charger typiquement des chaînes de caractères à afficher pour les labels de votre application. C’est limité car basé uniquement sur des Strings, et unidirectionnel : il n’est pas possible d’enregistrer des préférences dans un ResourceBundle par exemple. Enfin, un ResourceBundle n’est qu’un fichier plat de type Properties, sans notions de hiérarchie.
Quels sont les apports de la JSR-296 ?
Tout d’abord la possibilité de stocker des chaînes, des images, des URL, des valeurs numériques, ou des valeurs hexadécimales (code couleur). Ensuite une notion d’héritage pour regrouper certaines ressources générales (le nom de votre produit) et de spécialiser dans des fichiers dérivés des propriétés particulières (nom d’une fenêtre, d’un label).
La class ResourceMap permet de convertir et de mettre en cache les propriétés de l’application. Pour cela elle se base sur la class StringConverter qui fait partie de ResourceMap. ResourceMap est un décorateur pour la class ResourceBundle qui elle-même finalement, est un décorateur pour Properties.
Le plus important (et je crois le moins simple à expliquer) c’est la notion d’héritage. Basé sur la représentation Package>Class.
Si vous voulez utiliser « gratuitement » l’injection de propriété, il faut suivre quelques règles. Il est temps de sortir un petit exemple :
Soit la class suivante qui affiche le nom et la couleur d’un fruit dans un JLabel
/src/main/java/com/touilleur/demo/TestJSR.java
package com.touilleur.demo ; Public Class TestJSR { Private JLabel myLabel = new JLabel(“myLabelTest”,”Fraise”); … }
Pour pouvoir spécifier par exemple le label, mais aussi la font et la couleur du JLabel, il suffit de déclarer un fichier de Properties dans un répertoire resources en respectant la structure de package de la class TestJSR
/src/main/java/com/touilleur/demo/resources/TestJSR.properties
# Fichier de test myLabelTest.text=Une Orange de Seville myLabelTest.font=Arial-BOLD- 16 myLabelTest.foreground=142,20,65
Les ressources ne sont donc pas qu’internationalizable mais aussi configurable sans taper de code Java. Vous imaginez ici facilement la possibilité de passer d’une charte graphique à une autre en modifiant simplement les propriétés des éléments.
Ce qu’il manque, c’est une notion d’héritage de propriétés comme en CSS par exemple pour vraiment pouvoir fixer facilement l’aspect d’une interface.
Exemple complet
Voici un exemple qui utilise la classe SingleFrameApplication ti
src/main/java/com/touilleur/demo/SayHello.java
package com.touilleur.demo ; /** * Exemple simple JSR-296 */ public class SayHello extends SingleFrameApplication { protected void startup(String[] args) { JButton btn = new JButton(); btn.setName( "testButton" ); JPanel panel = new JPanel(); panel.add( btn ); show( panel ); } public static void main( String[] args ) { Application.launch( SayHello.class, args ); } }
Nous pouvons associer le fichier de propriétés suivant afin de définir les caractéristiques de l’interface facilement :
Application.title=SayHello Application.id=SayHello1 testButton.font=times italic 12 testButton.foreground=255,0,0 testButton.Action.text = &Afficher un message
Ceci va créer un JLabel en rouge, sur lequel l’utilisateur poura cliquer pour afficher un message. Justement parlons maintenant des Actions.
Gestion des Actions
Les actions en Swing sont les événements que l’utilisateur déclenche dans l’interface. Pour cela une nouvelle annotation @Action permet de marquer une méthode comme étant une action.
Si j’ajoute une méthode sayHello à mon code pour changer le label du bouton, voici comment la déclarer. J’ai récuperé un exemple publié par l’un des auteurs de la JSR-296 et je l’ai adapté.
Notez l’ajout d’une méthode sayHello qui retourne une SayHelloTask.
package com.touilleur.demo ; /** * Exemple simple JSR-296 */ public class SayHello extends SingleFrameApplication{ protected void startup(String[] args){ ApplicationActionMap applicationMap = ApplicationContext.getInstance().getActionMap( getClass(), this ); ResourceMap resourceMap = ApplicationContext.getInstance().getResourceMap( getClass() ); JLabel label = new JLabel(); label.setName( "titleLabel" ); JButton button = new JButton(); button.setName( "testButton" ); button.setAction(applicationMap.get( "startCounting" ) ); JPanel panel = new JPanel(); panel.setBorder( BorderFactory.createTitledBorder(resourceMap.getString( "countingDemo" ) ) ); panel.setLayout( new BorderLayout() ); panel.add( label, BorderLayout.NORTH ); panel.add( button, BorderLayout.CENTER ); show( panel ); } @Action public SayHelloTask sayHello( ActionEvent ev ){ return new SayHelloTask(); } public static void main( String[] args ) { Application.launch( SayHello.class, args ); } }
Vous pouvez aussi conditionner l’exécution d’une Action à la valeur d’une propriété. Comment ne pas appeler la méthode « sayHello » si la proprieté « speak » est à false ?
Simplement en utilisant l’attribut « enabledProperty » comme sur cet exemple
@Action(enabledProperty = "speak") public SayHelloTask sayHello( ActionEvent ev ) { return new SayHelloTask(); }
Voyons pour terminer la partie Task et j’aurai fait le tour des caractéristiques les plus importantes du framework Swing.
public class SayHelloTask extends Task{ public SayHelloTask (){ super(SayHelloTask.class); } public Void doInBackground() throws InterruptedException { System.out.println("Je prépare ma réponse tranquillement..."); while(!isCancelled()) { Thread.sleep(2000); publish((Void)null); } return (Void)null; } public void process(List ignored) { long elapsedTime = getExecutionDuration(TimeUnit.MILLISECONDS); System.out.println("Elapsed" + elapsedTime); } }
Cependant ici, si je viens à cliquer plusieurs fois sur mon bouton, je vais déclencher autant d’évenement que de click dans l’utilisateur. Or parfois vous voulez bloquer l’interface ou au moins une partie le temps du traitement.
Grâce au framework nous pouvons facilement désactiver par exemple notre bouton en utilisant l’enum Block.COMPONENT comme sur cette exemple
@Action(enabledProperty = "speak", block = Block.COMPONENT) public SayHelloTask sayHello( ActionEvent ev ) { return new SayHelloTask(); }
Enregistrement de l’état
Cette fonction permet de persister la position des fenêtres, vos préférences d’affichage, le fait d’avoir changé la taille d’une colonne d’un JTable par exemple. Très pratique et évite de devoir tout implémenter soit-même.
En conclusion
Swing Application Framework est un framework léger et simple qui se propose de simplifier l’écriture des applications Swing. Des abstractions simples permettent d’écrire plus rapidement du code robuste et performant, car la gestion des threads est simplifiée.
Je n’ai pas parlé de l’injection avec le tag @Resource. C’est une initiative de Romain Guy au départ (voir mes anciens posts) et cette idée permet vraiment de simplifier l’injection de ressource comme des icônes dans votre code.
Comme je dis à chaque fois : à tester donc.
Ressources supplémentaires:
Présentation complète de JSR-296 http://developers.sun.com/learning/javaoneonline/2006/desktop/TS-3399.pdf
http://weblogs.java.net/blog/diverson/archive/2007/04/swing_applicati.html
Voir aussi le blog d’Hans Muller de SUN.
0 no like