1°/ Objectifs

L'objectif de ce TP est de reprendre l'application du TP n°1 et d'y ajouter le code qui gère l'appui sur le bouton "Moyenne". Dans un deuxième temps, nous allons modifier le code de la fenêtre principale, et en particulier, nous allons lui ajouter un menu permettant à l'utilisateur de basculer entre les interfaces graphiques versions 1 et 2.


2°/ Action du bouton Moyenne

Afin de gérer l'action du bouton Moyenne, trois ajouts doivent être faits :

  1. Créer une classe ControlButton, dont le rôle est d'écouter le bouton "Moyenne", afin de déclencher le calcul de la moyenne lorsque l'utilisateur clique sur ce bouton.
  2. Créer une méthode addListeners() qui sera appelée après l'initialisation des attributs et qui associera une instance de ControlButton au bouton moyenne pour capturer les évènements ActionEvent qu'il génère.
La classe ControlBouton va contenir le code associé au calcul de la moyenne. En résumé, ce calcul consiste à récupérer les notes et coefficients entrés par l'utilisateur, puis à calculer la moyenne. Par conséquent, la classe ControlBouton a besoin d'avoir accès à la classe Main pour pouvoir lire l'état de certains composants graphiques (qui sont des attributs de Main). 
 

3°/ Contenu de la classe ControlBouton

Lorsque l'on clique sur un Button, un évènement de type ActionEvent est généré. Donc, pour écouter un tel évènement, nous avons besoin d'un objet qui implémente l'interface EventHandler<ActionEvent>. De ce fait, cette classe doit contenir non seulement un constructeur (qui prend en paramètre un objet de la classe Main) mais également définir la méthode handle(ActionEvent e). Cette méthode sera automatiquement appelée lorsque un évènement de type ActionEvent est généré par le bouton écouté. C'est donc cette méthode qui contient le code associé au calcul de la moyenne.


Dans un premier temps, nous allons nous assurer que l'évènement clic est bien récupéré par l'écouteur du bouton. Pour ce faire, ajouter uniquement :

System.out.println( "clic sur le bouton'' );

dans le corps de la méthode handle(). Si cela fonctionne, effacer cette ligne et remplissez la méthodes en suivant les étapes indiquées ci-dessous :

  1. on récupère les notes (Anglais, Math, Info, Géo et éventuellement Option) sous forme de String dans les TextField,
  2. s'il manque une note non optionnelle, on affiche un message d'erreur dans une fenêtre de dialogue et on arrête le traitement,
  3. sinon on convertit les notes en double,
  4. si un de ces notes est inférieure à 0 ou supérieure à 20, on affiche un message d'erreur dans une fenêtre de dialogue et on arrête le traitement,
  5. on vérifie si la case à cocher "Prendre en compte les coefficients est cochée". Si non, mettre tous les coefficient à 1 et aller directement en 8.
  6. on récupère les coefficients de chaque matière, sous forme de double, en vérifiant quel bouton est coché,
  7. on vérifie quelle option est prise et on en déduit un coefficient : 2 pour latin et grec, 1 pour sport.
  8. on calcule la somme des coefficients en prenant garde de ne pas ajouter celui de l'option si la note d'option n'existe pas.
  9. on calcule la moyenne générale en faisant la somme de chaque note (sauf éventuellement celle d'option) multipliée par son coefficient et en divisant le total par la somme des coefficients calculée en 7.
  10. on change le label "Valeur" pour afficher la moyenne.
Attention, il y a des exceptions qui peuvent être générées lors de ces calculs, notamment lorsque vous convertissez des String en double. Il faudra donc capturer toutes ces exceptions. Chaque capture devra générer une fenêtre affichant un message d'erreur explicite.

 

Pour gérer l'affichage des messages d'erreur, écrivez une méthode creerDialogErr(String messageErr) qui prend en paramètre le message d'erreur à afficher. Cette méthode crée un Alert  (cf. exemple du cours n°1) afin de permettre le blocage de l'utilisation de la fenêtre principale tant que la fenêtre contenant le message d'erreur est ouverte. A noter que creerDialogErr() doit se situer dans la classe Main pour fonctionner correctement.



4°/ Modification de la fenêtre principale

Les premières modifications concernent l'apparence de l'interface. Vous devez faire les modifications suivantes :

  • ajouter un icone (téléchargez le fichier icone-fenetre.gif) et une position initiale,
  • rendre la fenêtre non redimensionable.

La figure ci-dessous représente la fenêtre que l'on souhaite obtenir.


menu

Vous devez ensuite créer un menu, composé de trois items. Les deux premiers items, nommés "Version 1" et "Version 2", permettent de changer dynamiquement l'interface graphique de l'application : on peut choisir entre les versions 1 et 2 implémentées dans le TP n°1.  Le troisième item est un sous-menu qui se nomme "Aide" et qui s'ouvre lorsque l'on passe la souris dessus. Ce sous-menu comporte deux sous-items nommés "Comment ça marche ?" et "A propos". Vous devez implémenter graphiquement le menu dans sa totalité, mais gérer uniquement les évènements des deux premiers items. Le rendu est représenté sur la figure ci-dessus.

Pour obtenir ce résultat :
  • ajouter les attributs version1 et version2 de type MenuItem dans la classe Main. Ajoutez aussi l'attribut menuBar de type MenuBar.
  • Créer le menu dans la méthode createMenu en instanciant les Menu, MenuItem et MenuBar. Vous associerez les icônes icone-menu-version1.gif et icone-menu-version2.gif respectivement à version1 et version2.
  • Ajouter le menuBar à la fenêtre dans les méthodes addWidgetsToSceneV1() et addWidgetsToSceneV2() (cf. cours n°1).
Pour gérer les clics sur les items version1 et version2 , nous avons besoin de gérer des évènements du type ActionEvent. Quand bien même il y a deux items, nous n'allons utiliser qu'un seul listener. Pour cela :
  • créer une classe ControlMenu qui implémente l'interface EventHandler<ActionEvent> et définit la méthode handle(ActionEvent)  qui génère juste un affichage sur le terminal. Le contrôleur doit avoir accès aux composants de la classe Main pour pouvoir les modifier.
  • associer une instance de la classe ControlMenu aux deux MenuItem dans la classe Main. On utilise donc le même  listener pour deux objets distincts.
  • tester que l'affichage sur le terminal a bien lieu lorsque l'on clique sur les items du menu.
Il nous reste maintenant à modifier la méthode handle(). Il faut savoir quel est l'item sur lequel l'utilisateur a cliqué, afin de choisir si l'on doit passer à la version 1 ou à la version 2 de l'interface graphique.  Pour ce faire, utiliser la méthode getSource() de la classe ActionEvent. Ensuite, il faut effectivement appliquer le changement d'interface. Pour cela :
  • créer une méthode changerVersion(int version) dans la classe Main qui prend en argument le numéro de la version voulue (1 ou 2).
  • cette méthode :
    • supprime tout le contenu de la scène,
    • en fonction de la valeur X de version, appeler addWidgetsToSceneVX() définie dans le TP n°1,
    • appel primaryStage.setScene() 
Entrer des notes, calculer une moyenne, puis changer de version de l'interface. Que constatez-vous ? Pourquoi obtient-on ce phénomène ?
 
5°/ Gestion des évènements avec des classes internes
 
Refaire le TP 2, en utilisant les classes internes anonymes à la place des classes externes ControlBouton et ControlMenu.
 
6°/ Utiliser les filters et les handlers
 
Pour le calcul de la moyenne, remplacez la convenience method "setOnAction()" par un filtre "addEventFilter()" qui vérifie les données saisies. Puis ajoutez un gestionnaire d'évènement au bouton moyenne "addEventHandler()" pour calculer la moyenne. Cette section démontre que les filtres capturent les évènements avant les gestionnaires d'évènement.