FANTAR SAMI SRC1 B1 TP n°7 : Composants SWING et gestion d’évenements Exercice 1. Fenêtre principale de l’application 1. Le nom du package à importer pour pouvoir utiliser des composants Swing est le package "javax.swing.*;" l'astérisque permet de dire qu'on prend toutes les méthodes du package On l'importe au début du programme ainsi que deux autres composants nécessaires "java.awt" et "java.awt.event". "java.swing" contient l'ensemble des composants "allégés" qui ont le maximum de probabilités de fonctionner de la même façon sur toutes les plateformes. "java.awt" contient toutes les classes nécessaires pour créer des interfaces utilisateurs et pour l'utilisation d'images. Elle permet aussi de dessiner. "java.awt.event" contient les interfaces et les classes nécessaires pour communiquer avec les composants d' awt. Composants Swing de la classe : "JFrame" permet de créer une nouvelle fenêtre. "ActionListener" permet d'invoquer la méthode adéquate lors d'une action. "JLabel" est une zone de texte ou une image ou les deux. "JTextField" est un composant allégé permettant d'éditer une ligne de texte. "JButton" est un bouton permettant de réaliser des actions lors de la pression sur ce dernier. "JTextArea" est une zone de texte de plusieurs lignes. "Dimension" est une classe encapsulée permettant de spécifier la hauteur et la largeur d'un composant dans un seul objet. "Color" permet de spécifier la couleur selon un code RGB. "Font" permet de spécifier la couleur de fond permettant de rendre un texte visible. "FlowLayout" permet d'arranger le positionnement des composants de gauche à droite, comme les lignes d'un texte dans un paragraphe. 2. La méthode "random" de la classe "Math" permet de donner un nombre de manière aléatoire. Elle retourne une valeur de type double avec un signe positif plus grand ou egal à 0.0 et inférieur à 1.0 3. Avant l’ajout de la méthode « pack », nous obtenions ceci : Nous rajoutons dans le code du constructeur un appel à la méthode "pack". La classe PrixLot hérite de la classe JFrame et la classe JFrame hérite de la classe Window donc de toutes les méthodes de cette dernière. Nous recompilons et executons : La méthode pack appartient à la classe Window et permet de redimensionner la fenêtre par rapport au nombre d'éléments contenus dans la fenêtre. Exercice 2. Gestion de l’interaction sur la fenêtre du jeu 1.Bouton et fermeture de la fenêtre Lorsqu'on essaie de fermer la fenêtre en cliquant sur le bouton de fermeture de la fenêtre il ne ce passe rien. On rajoute donc la ligne "setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE)". Maintenant le bouton de fermeture de la fenêtre fonctionne. 2. Test des valeurs entrées par le joueur (a) Pour gérer l'événement sur le bouton Go! il faut implémenter l'interface ActionListener. Mais ActionListener oblige de déclarer ensuite dans la classe PrixLot, la méthode actionPerformed(). PrixLot peut maintenant écouter des événements. Pour pouvoir écouter les événements du bouton go!, il faut ajouter un écouteur à la classe. On ajoute donc : bouton.addActionListener(this). « This » permet de dire que l'écouteur est la classe courante donc PrixLot devient l'écouteur. Dorénavant, l'écouteur permet d'appeler la méthode actionPerformed() dès qu' un événement apparaît et lui passe cet événement en argument. Puis on vérifie la source de l'événement passé en argument On vérifie ensuite la source de l'événement passé en argument pour exécuter des instruction diverses en fonction. Cela permet donc à la classe d'écouter plusieurs boutons en même temps et de leur attribuer des actions. (b) public void actionPerformed(ActionEvent unEvnt) { if (unEvnt.getSource() == goButton) // Le joueur clic sur Go ! { // On récupere la valeur saisie par le joueur pour la comparer String valeur_joueur = tfSaisie.getText(); try { // On converti la chaîne valeur_joueur en int int nbTape =Integer.parseInt(valeur_joueur); if (nbTape==nbCache) //Cas où le joueur trouve d’un coup { taResultat.setForeground(Color.BLUE); taResultat.setFont(new Font("Serif",Font.BOLD,14)); taResultat.setText("Bravo! Tu as trouvé du 1er coup!"); } else { if (nbTape<nbCache)//Cas où nombre taper < nombre caché { taResultat.setForeground(Color.GREEN); taResultat.setFont(new Font("Serif",Font.BOLD,12)); taResultat.setText("trop petit"); } else //Cas où nombre taper > nombre caché { taResultat.setForeground(Color.YELLOW); taResultat.setFont(new Font("Serif",Font.BOLD,18)); taResultat.setText("trop grand"); } } } catch (NumberFormatException exc) { //Verification de la saisie du joueur System.out.println("Vous n’avez pas saisie un entier") ; } } } 3. Gestion du nombre d'essais public void actionPerformed(ActionEvent unEvnt) { if (unEvnt.getSource() == goButton// Le joueur clic sur Go ! { int k = nbEssaisAutorises - nbEssais; if (nbEssais == nbEssaisAutorises) //cas où nombre d’essais effectués =nombre d’essais autorisé { taResultat.setForeground(Color.RED); taResultat.setFont(new Font("Serif",Font.BOLD,14)); taResultat.setText("Vous n'avez plus la possibilité de rejouer! Vous avez perdu...");//msg d’erreur signalant que le jeu est fini tfSaisie.setEditable(false);//blocage de la saisie de tfsaisie } else // cas où nombre d'essais effectués < nombre d'essais autorisé { String valeur = tfSaisie.getText();// Récupération de la valeur tapée par le joueur try { // On converti la chaîne valeur_joueur en int int nbTape =Integer.parseInt(valeur); if (nbTape==nbCache) //Cas où le joueur trouve d’un coup { tfSaisie.setEditable(false); taResultat.setForeground(Color.GREEN); taResultat.setFont(new Font("Serif",Font.BOLD,14)); taResultat.setText("Bravo! Vous avez deviné en "+nbEssais+" essais."); } else { if (nbTape<nbCache)//Cas où nombre taper < nombre caché { taResultat.setForeground(Color.BLUE); taResultat.setFont(new Font("Serif",Font.BOLD,12)); taResultat.setText("trop petit... Attention il ne vous reste plus que "+k+"essais..."); } else //Cas où nombre taper > nombre caché { taResultat.setForeground(Color.ORANGE); taResultat.setFont(new Font("Serif",Font.BOLD,18)); taResultat.setText("trop grand… Attention, il ne vous reste plus que "+k+" essais..."); } } } catch (NumberFormatException exc) { //Verification de la saisie du joueur System.out.println("Vous n’avez pas saisie un entier"); } nbEssais = nbEssais+1; } } } 4. Gestion des erreurs de saisie (a) Lorsque le jouer ne saisie pas une valeur entière dans le JtextArea tfSaisie, le terminal affiche un message d’erreur « Vous n’avez pas saisie un entier». Le bloc try{…} catch(NumberFormatException exc){…} dans la méthode actionPerformed permet de vérifier que le type de données saisie correspond au bon format. Dans notre cas, le format attendu est un integer (NumberFormat) le mot clé TRY permet de préciser un morceau du code sur lequel on s'attend à qu'une erreur (exception) se présente. Le mot clé CATCH sert à spécifier le code à exécuter pour une exception (ou une catégorie)donnée. Il suffit alors de faire suivre le mot catch d'une parenthèse ouvrante, d'un type exception (une classe) du nom qu'on lui donne (tout comme un paramètre de fonction), d'une parenthèse fermante, et du code associé placé entre accolade. Si un événement indésirable survient dans le bloc try, la partie éventuellement non exécutée de ce bloc est abandonnée et le premier bloc catch est traité. Si catch est défini pour capturer l'exception issue du bloc try alors elle est traitée en exécutant le code associé au bloc. Si le bloc catch est vide (aucune instruction entre les accolades) alors l'exception capturée est ignorée. (b) catch (NumberFormatException exc) { //Verification de la saisie du joueur JOptionPane.showMessageDialog (this ," Entrez un nombre entier !!!", "Attention !", JOptionPane.WARNING_MESSAGE ); } public static void showMessageDialog(Component parentComponent, Object message,String title,int messageType)throws HeadlessException On obtient un message d’erreur qui montre un message en utilisant une icône de défaut déterminée par le paramètre de messageType lorsque l’utilisateur saisie autre chose qu’un nombre entier. Les différents paramètres du panneau : parentComponent - détermine la vue dans laquelle le dialogue est montré ; si nulle, ou si parentComponent n'a aucune vue, une vue de défaut est employé message – texte à afficher (ici, Entrez un nombre entier !!!) title - titre de la boite de message (ici, on l’appel Attention !) messageType - type de messsage que le montre : ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE, selon le type de message, l’icône d’alerte change. Exercice 3. Ajout d’un menu à la fenêtre principale 1. La classe MenuPrixLot.java crée un menu pour la fenêtre princiale .La méthode actionPerformed permet d'exécuter des actions lorsque l'on clique sur l'une des rubriques du menu. Explication des différents composants et méthodes utilisées : JFrame() : Crée une fenêtre invisible sans titre. JMenu () : Créer un menu pouvant être inséré dans une barre de menu. JMenuItem () :Les éléments du menu sont représenté par des objets MenuItem. add () : ceci permet d’ajouter des éléments au menu. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class MenuPrixLot extends JMenuBar implements ActionListener { JMenuItem item1, item2, item3; PrixLot fenAppli; public MenuPrixLot(PrixLot frame) { //On associe le menu à l’application principale fenAppli = frame ; JMenu menu1, menu2; menu1 = new JMenu("Fichier"); item1= new JMenuItem("réinitialiser"); item2= new JMenuItem("quitter"); menu1.add(item1); menu1.add(item2); add(menu1); menu2 = new JMenu("Aide"); item3= new JMenuItem("à propos"); menu2.add(item3); add(menu2); //MenuPrixLot joue le role d’écouteur sur les différents items du menu. } public void actionPerformed(ActionEvent unEvnt) { //ici, on gère les évenements liés au Item } } (2) On rajoute à la fin de la méthode PrixLot, le code suivant permettant d’intégrer l’objet MenuPrixLot dans la classe PrixLot. JMenuBar in_barre= new MenuPrixLot(this); setJMenuBar(in_barre); Classe JMenuBar: elle représente une barre de menu ou un groupe de menu. JMenuBar. setJMenuBar : permet d’inserer la barre de menu dans le programme courant. Exercice 4. Gestion de l’intéraction liée au menu import java.awt.*; import java.awt.event.*; import javax.swing.*; public class MenuPrixLot extends JMenuBar implements ActionListener { JMenuItem item1, item2, item3; PrixLot fenAppli; public MenuPrixLot(PrixLot frame) { //on associe le menu à l’application principale fenAppli = frame ; //On crée les différents menu fichier et aide JMenu menu1, menu2; menu1 = new JMenu("Fichier"); menu2 = new JMenu("Aide"); //On crée les différents items du menu1 item1= new JMenuItem("réinitialiser"); item2= new JMenuItem("quitter"); //On ajoute les items crée au menu1 menu1.add(item1); menu1.add(item2); //On ajoute le menu1 crée à la barre de menu de l’application add(menu1); //On crée les différents items du menu2 item3= new JMenuItem("à propos"); //On ajoute les items crée au menu2 menu2.add(item3); //On ajoute le menu2 crée à la barre de menu de l’application add(menu2); //MenuPrixLot joue le rôle d’écouteur sur les items du menu item1.addActionListener(this); item2.addActionListener(this); item3.addActionListener(this); } public void actionPerformed(ActionEvent unEvnt) { //On gère les événements de l’item2 du menu if (unEvnt.getSource() == item2) java.lang.System.exit(0); //On gère les événements de l’item3 du menu if (unEvnt.getSource() == item3) JOptionPane.showMessageDialog(this, "-=[$@m$0ft CORP]=-", "à propos", JOptionPane.INFORMATION_MESSAGE ); //On gère les événements de l’item1 du menu if (unEvnt.getSource() == item1) { fenAppli.nbEssais = 1; // on affiche un message dans JTextAera taResultat fenAppli.taResultat.setText(" Attention, vous n'avez que "+fenAppli.nbEssaisAutorises+" essais... Soyez stratégiques !"); // on attribut les paramètres demandés d'affichage du texte fenAppli.taResultat.setForeground(Color.RED); fenAppli.taResultat.setFont(new Font("Serif",Font.BOLD,14)); fenAppli.taResultat.setEditable(false); // Recherche aléatoire d’un nombre entre 1 et 100 fenAppli.nbCache = (int)(Math.random()*100)+1; // Le nombre aléatoire sorti, apparaît dans le terminal System.out.println("Le nombre à deviner est "+fenAppli.nbCache+"."); // on attribut les paramètres demandés d'affichage du texte fenAppli.tfNbCache.setEditable(false); fenAppli.tfNbCache.setBackground(Color.YELLOW); fenAppli.tfNbCache.setForeground(Color.DARK_GRAY); //On efface le contenu de JTextField tfSaisie fenAppli.tfSaisie.setText(""); //On peut réécrire dans JTextField tfSaisie car on met setEditable à true fenAppli.tfSaisie.setEditable(true); } } } Exercice 5. Un peu de maths… D’après des connaissances personnelles vu l’année dernière en DUT INFO à Paris5, la recherche dichotomique me semble la plus adapté comme stratégie pour trouver la valeur recherché en 6 coups. Principe : on tape l’élément au milieu de la liste. Dans notre cas, la liste va jusqu’à 100. On tape donc 50. - si sa clé est égale à la valeur cherchée, c'est gagné - si sa clé est inférieure à la clé cherchée, il ne reste à traiter que la moitié droite de la liste. Donc on tape la moitié de 50 qui est 25. - si sa clé est supérieure à la clé cherchée, il ne reste à traiter que la moitié gauche de la liste Donc on tape la moitié au dessus de 50 qui est 75. On continue ainsi la recherche en diminuant à chaque fois de moitié le nombre d'éléments de la liste restant à traiter. Remarque : cette méthode n'est pas du tout adaptée à une implémentation chaînée puisqu'il n'y a pas d'accès direct au k-ème élément. Exemple : le chiffre recherché est 10 - 1er essais : 50 -> c’est trop grand donc on réduit le champ de recherche entre 1 et 50. - 2ème essais : On prend la moitié de 50 soit 25 -> c’est trop grand donc on réduit le champ de recherche entre 1 et 25. - 3ème essais : On prend la moitié de 25 soit 13 -> c’est trop grand donc on réduit le champ de recherche entre 1 et 13. - 4ème essais : On prend la moitié de 13 soit 7 -> c’est trop petit donc on réduit le champ de recherche entre 7 et 12. - 5ème essais : 12-7=5 donc on ajoute la moitié de 5 à 7 se qui donne 10 -> c’est gagné. L’algorithme correspondant à la recherche dichotomique est le suivant : Procedure recherche_dichotomique(par val ent elt, par val ent N, par val ent T[]) Debut var inf, sup, m; inf <- 1; sup <- N; m <- (inf+sup) div 2; /* en C++ m = (int)((inf+sup)/2) */ /* Ici l'astuce : la borne supérieure ou inférieure est modifiée, le tableau n'est plus parcouru dans son ensemble */ Tant que (T[m] != elt et inf < sup) faire Si (elt < T[m]) alors sup <- m - 1; Sinon inf <- m + 1; Fin Si m <- (inf + sup) div 2; Fin Tque Si (T[m] = elt) Afficher("L'element se trouve à l'indice m") Sinon Afficher ("L' élément n'existe pas ") Fin Si Fin Exercice 6. Finalisation de l’application Le code suivant se trouvant dans le constructeur de la classe PrixLot soit la méthode PrixLot : //On ajoute dans le conteneur principal, les composants Container Conteneur = getContentPane(); Conteneur.setLayout(new FlowLayout()); Conteneur.add(lDevine); Conteneur.add(tfNbCache); Conteneur.add(lSaisie); Conteneur.add(tfSaisie); Conteneur.add(goButton); Conteneur.add(taResultat); Conteneur.setBackground(Color.WHITE); est remplacé par le code ci-dessous pour pouvoir gérer l'apparence graphique avec des BoxLayout : //On crée le conteneur de la fenetre de l’application Container conteneur = getContentPane(); //on crée les objets BoxLayout JPanel BoxV = new JPanel(); BoxV.setLayout(new BoxLayout(BoxV, BoxLayout.Y_AXIS)); JPanel Box1 = new JPanel(); BoxH1.setLayout(new BoxLayout(Box1, BoxLayout.X_AXIS)); JPanel Box2 = new JPanel(); BoxH2.setLayout(new BoxLayout(Box2, BoxLayout.X_AXIS)); JPanel Box3 = new JPanel(); BoxH3.setLayout(new BoxLayout(Box3, BoxLayout.X_AXIS)); JPanel Box4 = new JPanel(); BoxH4.setLayout(new BoxLayout(Box4, BoxLayout.X_AXIS)); //On ajoute aux BoxLayout JLabel, JTextField, JButton Box1.add(lDevine); Box1.add(tfNbCache); Box2.add(lSaisie); Box2.add(tfSaisie); Box3.add(goButton); Box4.add(taResultat); //On met la couleur de background en blanc Box1.setBackground(Color.WHITE); Box2.setBackground(Color.WHITE); Box3.setBackground(Color.WHITE); Box4.setBackground(Color.WHITE); //On ajoute les 4 BoxLayout horizontales à la BoxLayout verticale (BoxV) BoxV.add(Box1); BoxV.add(Box2); BoxV.add(Box3); BoxV.add(Box4); // On ajoute BoxV au conteneur de l’application conteneur.add(BoxV); conteneur.setBackground(Color.WHITE);