Première partie

Objectifs d'apprentissage

  • Décrire la programmation évènementielle.
  • Concevoir une application utilisateur graphique simple.
  • Implémenter une fonction de rappel («callback»).
  • Appliquer le concept d'héritage pour concevoir une application.
  • Concevoir une classe qui réalise une interface.

Les composantes ("component") versus les conteneurs ("container")

Afin de programmer des interfaces graphiques, vous vous devez de comprendre le concept de composantes et de conteneurs. Chaque objet en GUI joue un rôle précis dans l’aspect visuel de votre application.

  • Les composantes (Component) graphiques telles que les champs de texte (JTextField), les boutons (JButton), les listes (JList), les étiquettes (JLabel) et bien d’autres. Ces dernières sont des éléments de base de votre interface qui doivent se trouver dans un conteneur (Container).

  • Les conteneurs (Container) comme les cadres (JFrame), les boîtes de dialogue (Jdialog), les panneaux (JPanel), et bien d’autres sont utilisés pour contenir les composantes élémentaires dans une disposition spécifique (LayoutManager). Notamment, on peut placer les éléments de gauche à droite et de haut en bas (FlowLayout), sous forme de grille (GridLayout), en séparant le conteneur en zone (BorderLayout) et bien d’autres . Un conteneur peut également contenir des sous-conteneurs.

Consultez la figure 0 ci-dessous . Il s’agit d’un exemple simple qui illustre les principes de base d’une interface graphique. Les panneaux (JPanel) dénotés par les rectangles bleus servent de conteneurs aux différentes composantes élémentaires. En rouge , il s’agit du cadre (JFrame) qui sert de conteneur pour les panneaux. Identifiez le type de disposition (Layout) utilisé pour chacun des 3 panneaux.

SimpleGUI Figure 0 : Les composantes GUI

Exercice de pratique

Prenez le temps de tester les différentes dispositions possibles au cours de ce laboratoire. La maîtrise des conteneurs et des composantes graphiques nécessitent de la pratique. Complétez le code ci-dessous afin d'optenir la même fenêtre que lafigure 1.

import javax.swing.*;
import java.awt.*;

 public class MyFirstGUI{

  public static void main(String[] args){
	 
	JFrame myFrame = new JFrame();
	//set the title
	myFrame.setTitle("My first Window");
	
	//set the size to 500X300 pixels (width by heigth)
	myFrame.setSize(500, 300);
	
	//set the object to the middle of our screen
	myFrame.setLocationRelativeTo(null);
	
	//end program when user closes the window
	myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	

	//create a panel
	JPanel myPanel = new JPanel();
	//add a label to the panel
	myPanel.add(new Label("My first panel"));
	//set panel background color
	myPanel.setBackground(Color.green);
	//add the panel to the frame
	myFrame.add(myPanel, BorderLayout.NORTH);
	
	//YOUR CODE HERE
	
	
	//set the window to visible      
    myFrame.setVisible(true);
	}

}
SimpleGUIExample Figure 1 : Ma première interface usager

    Les « packages »

    Afin de rendre les classes accessibles dans votre unité de compilation, vous devrez faire l'usage de plusieurs import. Deux paquets vous seront fort utiles lors de la programmation d'interface graphique usager :

    • Le paquetage java.swing contient les classes graphiques swing de base:
      • Les classes de composantes GUI (telles que JButton, JTextField et JLabel)
      • Les classes de conteneur GUI (telles que JFrame, JPanel, JDialog et JScrollPane)
      • Les gestionnaires de mise en page (tels que FlowLayout, BorderLayout et GridLayout)
    • Le paquetage java.awt contient les classes personnalisées (telles que Graphique, Color et Font). La gestion des évènements est aussi prise en charge par ce package.

    Il est possible en Java d'importer un paquet et toutes les classes qu'il contient en utilisant une étoile « * » plutôt que le nom de la classe désirée seulement (voir exemple ci-dessous). Il ne s'agit pas d'une bonne pratique puisqu'à grande échelle, vous importez des classes inutiles à votre programme qui ralentiront son temps d'exécution.

    import javax.swing.*; 

    La programmation évènementielle

    Tel que mentionné à la dernière partie package java.awt.event prend en charge la gestion des évènements. Pour ce lab, nous utiliserons :

    • Les classes d'évènements (telles que ActionEvent, MouseEvent, KeyEvent et WindowEvent)
    • Interfaces d'écoute d'évènements (telles que ActionListener, MouseListener, KeyListener et WindowListener),

    Voici les concepts clés de la gestion d'évènements en Java :

    • Toutes les composantes graphiques peuvent être la source d'un évènement. Par exemple, un bouton génère un évènement («event») lorsque l'usager clique dessus ou encore un champ de texte génère un évènement lorsque l'usager appuie sur la touche « enter »
    • Toute classe peut gérer/contrôler («handler») un ou des types évènements. Il suffit :
      • D'implémenter la ou les méthodes d'une interface.
      • D'ajouter l'instance à la liste des gestionnaires d'évènements de la composante graphique qui est la source de l'évènement à gérer.

      Les objets gérant les évènements d'actions (par exemple cliquer sur un bouton) doivent implémenter l'interface ActionListener.

    • L'évènement généré par une composante source est envoyé à tous les gestionnaires qui se sont enregistrés auprès de cette composante.

      La source possède une méthode addActionListener ayant un paramètre de type ActionListener.

      Puisque le gestionnaire («listener/handler») implémente l'interface ActionListener, la source sait que le gestionnaire possède une méthode actionPerformed(ActionEvent event).

    Counter

    Réexaminons ensemble l’exemple du Counter que vous avez déjà vu en classe, cet exemple suit le concept de Model-Vue-Contrôleur

    Le modèle :

    Ce que cet exemple modélise est un Counter qui augmente un nombre entier de 1 ou le remet à 0 dépendamment du bouton qui est cliqué. La classe Counter implémente donc ce modèle avec les quelques méthodes nécessaires (increment, getValue, reset)

    public class Counter {
    	private int value ;
    	public Counter () {
    		value = 0 ;
    	}
    	public void increment () {
    		value++;
    	}
    	public int getValue () {
    		return value ;
    	}
    	public void reset () {
    		value = 0 ;
    	}
    	public String toString () {
    		return "Counter : { value="+value+"}" ;
    	}
    }
      

    La vue :

    Nous désirons avoir plusieurs types de vue (2), mais nous ne voulons pas que le programme interagisse différemment avec les différentes vues. Nous créons donc une interface View. Dans ce cas, nous pourrons être certain que toutes les classes implémentant l’interface View auront la méthode void update().

    public interface View {
    	void update () ;
    }
      

    Il y a deux types de vue qui implémente l’interface View. La première, TextView, est une représentation écrite du Counter. Notez que la classe implémente la méthode void update.

    public class TextView implements View {
    	private Counter model ;
    	public TextView (Counter model) {
    		this.model = model ;
    	}
    	public void update() {
    		System.out.println(model.toString()) ;
    	}
    }
      

    La deuxième vue est la GraphicalView, cette vue est une interface graphique, elle dérive de JFrame en utilisant le mot clé « extends », c’est ce qui permet a la classe d’être représenté graphiquement.

    Dans le constructeur de cette vue, nous inclurons en paramètre le modèle (Counter) ainsi que le contrôleur (Controller) (vue plus bas). Dans le constructeur, nous utiliserons « setLayout (new GridLayout(1 ,3)); » qui divise la fenêtre en une rangée et 3 colonnes. Cela crée trois positions où nous pourrons insérer des éléments comme des boutons. Pour ajouter des boutons, nous suivrons les quatre étapes suivantes :

    • Déclaration du bouton (de type JButton)
    • Initiation du bouton
    • Ajouter le gestionnaire d’évènements (ActionListener) au bouton
    • Ajouter le bouton au Jframe

    Nous pouvons aussi ajouter du texte avec l’utilisation d’un JLabel. Nous devrons aussi ajouter quelques lignes afin d'initialiser proprement la fenêtre :

    • « setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); » indique que l’application se terminera si l’on ferme la fenêtre.
    • « setSize(300, 100); » indique la taille de la fenêtre (axe x, axe y)
    • « setVisible(true); » fait en sorte que la fenêtre soit visible.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class GraphicalView extends JFrame implements View {
    	private JLabel input;
    	private Counter model;
    	public GraphicalView (Counter model, Controller controller) {
    		setLayout (new GridLayout(1 ,3));
    		this.model = model;
    		JButton button;
    		button = new JButton("Increment");
    		button.addActionListener(controller);
    		add(button);
    		JButton reset;
    		reset = new JButton ( "Reset" );
    		reset.addActionListener ( controller );
    		add(reset);
    		input = new JLabel ();
    		add(input);
    		
    		//setup
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setSize(300, 100);
    
    		//display the window
    		setVisible(true);
    	}
    	public void update () {
    	input.setText(Integer.toString(model.getValue())) ;
    }
    }
      

    Contrôleur :

    Nous avons finalement le contrôleur Controller. Le contrôleur est la classe qui s’occupe de la logique et de la communication de l’application. La classe doit implémenter ActionListener afin de pouvoir réagir à l’action lancée lorsque l’usager clique sur un bouton.

    Ce contrôleur possède une liste de View (View[]). Lorsque le contrôleur est créé, il enregistre un GraphicalView et un TextView dans sa liste de View. Il aura aussi une méthode update qui appelle la méthode update() de chacune des View qu’il a enregistrées.

    La méthode ActionPerformed(ActionEvent e) est appelée a chaque fois que l’interface graphique lance une action. La méthode vérifie quelle action a été lancée et réagit en conséquence, puis finalement met a jour (update) les vues.

    import java.awt.event.*;
    
    public class Controller implements ActionListener {
    	
    	private Counter model;
    	
    	private View[] views;
    	private int numberOfViews;
    	
    	public Controller(){
    	
    		views = new View[2];
    		numberOfViews = 0;
    		model = new Counter() ;
    		register(new GraphicalView(model, this));
    		register(new TextView(model));
    		update();
    	}
    	private void register(View view) {
    		views[numberOfViews] = view;
    		numberOfViews++;
    	}
    	private void update() {
    		for (int i = 0; i < numberOfViews; i++) {
    			views[i].update();
    		}
    	}
    	
    	public void actionPerformed ( ActionEvent e ) {
    	
    	if(e.getActionCommand().equals("Increment")) {
    		model.increment ();
    	} else {
    		model.reset();
    	}
    	
    	update();
    	}
    }
      

    Application

    Finalement, nous avons la classe App qui possède une méthode main nous permettant d’exécuter ce Counter

    public class App {
    	public static void main(String[ ] args) {
    		Controller controller;
    		controller = new Controller();
    	}
    }
      

    Exercice 1. Timer

    Dans cet exercice, vous devrez faire une application avec une interface graphique Timer représentant une horloge.

    Le Timer a 6 boutons soit un qui augmente et un qui diminue pour chacun des éléments suivants: les heures, les minutes et les secondes.

    Pour créer le Timer, nous nous baserons sur l’exemple précédant, le Counter.

    Pour adapter le code de Counter afin qu’il implémente le nouveau problème de Timer, il faudra changer le modèle, la vue et le contrôleur.

    Le modèle :

    Le modèle précédant était la classe Counter. Puisque nous voulons maintenant avoir une horloge, nous allons la remplacer par la classe Timer. La classe Timer a :

    • Trois méthodes qui incrémentent : les heures, les minutes et les secondes respectivement
    • Trois méthodes qui décrémentent : les heures, les minutes et les secondes respectivement
    • Trois « getters » qui retournent : les heures, les minutes et les secondes respectivement
    • Il faut aussi s’assurer que de respecter le cycle de 24 heures :
      • Les heures sont entre 0 et 23
      • Les minutes sont entre 0 et 59
      • Les secondes sont entre 0 et 59
    public class Timer {
    	private int hours;
    	private int minutes;
    	private int seconds;
    
    	public Timer(){
    		hours = 0;
    		minutes = 0;
    		seconds = 0;
    	}
        
    	//Your code here
    	
    	public String toString () {
    		return "Timer "+hours+":"+minutes+":"+seconds;
    	}
    }
      

    La vue :

    Vous devez modifier la classe GraphicalView afin qu’elle crée 6 boutons (pour incrémenter/décrémenter les heures, minutes et secondes) ainsi qu’une étiquette (JLabel) qui indique le temps. Nous utiliserons une grille de 2 par 3 pour accommoder ces éléments.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class GraphicalView extends JFrame implements View {
    	private JLabel input;
    	private Timer model;
    	public GraphicalView (Timer model, Controller controller) {
    		setLayout (new GridLayout(2, 3));
    		
            
            //you code here
    		
    		
    		//setup
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setSize(700, 100);
    
    		//display the window
    		setVisible(true);
    	}
    	public void update () {
    	input.setText(model.toString());
    }
    }
      

    Le contrôleur :

    Dans le contrôleur, il faut modifier la méthode « public void actionPerformed ( ActionEvent e ) » pour accommoder les 6 évènements lancés par les 6 boutons de GraphicalView et appeler les méthodes de Timer correspondantes.

    import java.awt.event.*;
    
    public class Controller implements ActionListener {
    	
    	private Timer model;
    	
    	private View[] views;
    	private int numberOfViews;
    	
    	public Controller(){
    	
    		views = new View[2];
    		numberOfViews = 0;
    		model = new Timer() ;
    		register(new GraphicalView(model, this));
    		register(new TextView(model));
    		update();
    	}
    	private void register(View view) {
    		views[numberOfViews] = view;
    		numberOfViews++;
    	}
    	private void update() {
    		for (int i = 0; i < numberOfViews; i++) {
    			views[i].update();
    		}
    	}
    	
    	public void actionPerformed ( ActionEvent e ) {
    	
    	//your code here
    		
    	update();
    	}
    }
      

    Note : Vous devez également modifier la classe TextView afin qu’elle utilise le modèle Timer plutôt que celui de Counter

    Voici un exemple du résultat :

    paint

    Figure 2 : Timer

Deuxième partie

Exercice 2: Point rouge

Voici une application ayant une zone où s'affiche un point rouge ainsi que des boutons. Lorsque l'usager appuie sur le bouton gauche, le point se déplace vers la gauche ; de même, lorsque l'usager appuie sur le bouton de droite, le point se déplace vers la droite. Adapté d'un exemple de Decker & Hirshfield (2000) Programming.java.

2.1 Créer la représentation graphique

2.1.1 Display Area

Créez une sous-classe de la classe JPanel que vous nommerez DisplayArea. Un objet de la classe DisplayArea servira de canvas, une surface sur laquelle l'application dessinera. Plus tard dans ce laboratoire, nous ajouterons tout ce qu'il faut afin de déplacer un point ou un carré coloré sur cette surface.
Vous pouvez consulter la documentation sur la classe JPanel à l'adresse suivante : https://docs.oracle.com/javase/8/docs/api/javax/swing/JPanel.html

Afin de rendre le nom JPanel disponible dans cette unité de compilation, ajoutez la directive suivante au début du fichier, avant la déclaration de la classe :

import javax.swing.JPanel; 
  • Déclarez une constante (variable de classe «public», «static», et «final») nommée DISPLAY_SIZE, dont la valeur est 500.
  • Ajoutez un constructeur dans lequel vous ferez un appel à la méthode setBackground, afin de changer la couleur de l'arrière-plan de cette composante. Utilisez la couleur java.awt.Color.WHITE. Vous pouvez simplement utiliser Color.WHITE, si vous ajoutez une directive import java.awt.Color au début du fichier avec l'autre directive import.
  • Finalement, vous devez redéfinir la méthode getPreferredSize. Cette méthode retourne un objet de type java.awt.Dimension ou simplement Dimension si l'on ajoute la directive import java.awt.Dimension avec les autres directives import de cette unité de compilation. La méthode getPreferredSize doit retourner un nouvel objet Dimension dont la hauteur et la largeur sont DISPLAY_SIZE.

Qu'avons-nous obtenu ? Lorsqu'un objet DisplayArea sera créé, il aura toutes les caractéristiques des objets de la classe JPanel. Cependant, l'arrière-plan de la composante graphique sera blanc. Lorsque l'application voudra déterminer la taille de cette application graphique, il y aura un appel à la méthode getPreferredSize. La méthode retournera alors une objet Dimension dont la largeur et la hauteur sont de 500 pixels.

2.1.2 GUI (Graphical User Interface)

Créez une sous-classe de la classe JFrame que vous nommerez GUI. Ce sera la fenêtre principale de l'application.
Vous pouvez consulter la documentation sur la classe JFrame à l'adresse suivante : https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html

Vous savez maintenant qu'il faut ajouter une directive (import) au haut de l'unité de compilation afin de rendre le nom JFrame disponible. Sinon, il faudra utiliser le nom complet javax.swing.JFrame.

import javax.swing.JFrame; 

Ajoutez un constructeur à la classe GUI. Dans une application graphique, le constructeur est souvent l'endroit où l'on construit la représentation graphique de l'application. C'est logique, car le constructeur est appelé au moment de la création de l'objet. Voici le rendu visuel recherché :

le rendu visuel recherché. Figure 2 : le rendu visuel recherché.

Pour obtenir une telle fenêtre, vous devez ajouter à votre constructeur les éléments suivants:

  • Ajoutez un titre à la fenêtre. Il y a deux façons de changer le titre de la fenêtre : soit l'on appelle le constructeur de la superclasse et on lui passe le titre comme argument (une chaîne de caractères) ou bien on utilise la méthode setTitle.
  • Il est nécessaire d'ajouter un appel à la méthode suivante afin que l'on quitte l'application (appel à la méthode System.exit) lorsque l'usager ferme la fenêtre.
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    Vous pouvez consulter la documentation à l'adresse suivante pour obtenir plus d'information: https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html#setDefaultCloseOperation-int-

  • Créez un objet DisplayArea et ajoutez-le au centre de la fenêtre. Puisque la classe GUI est une sous-classe de la classe JFrame, nous pouvons utiliser les LayoutManager. Choisissez celui qui vous permettra d'ajouter l'objet DisplayArea dans la partie centrale de la fenêtre.
  • Ajoutez les boutons de contrôle au bas de la fenêtre. Pour ce faire, suivez les étapes ci-dessous :
    • Vous devez d'abord créer un objet JPanel. Il s'agira du conteneur des boutons. Assurez-vous que l'arrière-plan de cette composante graphique est blanc.
    • Créez quatre objets de la classe JButton. Utilisez les étiquettes «Left», «Right», «Up», et «Down».
    • Ajoutez ces boutons à l'objet de type JPanel. Notez que la disposition par défault d'un objet JPanel est le FlowLayout. Ce dernier place les objets de gauche à droite et de haut en bas respectant l'ordre dans lequel les objets ont été ajoutés.
    • Ajoutez l'objet de type JPanel à votre classe GUI. Comme cette dernière dernière est une sous-classe de JFrame, nous pouvons facilement ajouter notre panneau à l'endroit désiré.
  • Finalement, ajoutez les deux appels de méthode suivants au constructeur. Ces méthodes ont été héritées des classes parents. La méthode pack a hérité de la classe java.awt.Window, alors que la méthode setResizable a été héritée de la classe java.awt.Frame.
    setResizable(false); 
    pack();

    Le premier appel fait en sorte que la taille de l'application demeure fixe, alors que pack() détermine la taille idéale de la fenêtre en fonction de la taille idéale des sous-éléments de la fenêtre. Cet appel à la méthode pack() forcera un appel à la méthode getPreferredSize() de la classe DisplayArea.

  • Ajoutez une méthode principale (main) à la classe GUI. Cette méthode doit créer un objet de la classe GUI. La méthode main doit aussi rendre visible notre fenêtre. Pour ce faire, vous utiliserez la méthode setVisible comme dans l'exercice de pratique.
  • Compilez votre application et assurez-vous qu'elle ressemble à l'application ci-haut.


Si vous cliquez sur les boutons, vous verrez qu'il ne se passe rien. Il nous faudra ajouter un gestionnaire d'évènements afin d'associer des actions à chaque clique d'un bouton.

2.2 Gestionnaire d'évènements (Partie 1 de 3)

Vous devez maintenant modifier l'application afin de gérer les évènements. Pour ce faire, vous devrez modifier le constructeur de la classe GUI afin d'ajouter (enregistrer) un gestionnaire d'évènement pour chaque bouton. Quelle sera la classe responsable de ce travail ? Lorsqu'un bouton est cliqué, l'application doit modifier le canevas (DisplayArea). Modifiez la classe DisplayArea afin qu'elle serve de gestionnaire d'évènements semble donc un choix naturel.

  • Ajoutez une directive import afin de rendre le nom java.awt.event.ActionListener disponible à la classe DisplayArea.
  • Modifiez la déclaration de la classe DisplayArea afin de réaliser l'interface ActionListener. Compilez la classe dès maintenant. Quel message d'erreur obtenez-vous ?
  • Vous devriez obtenir un message comme celui-ci :
    DisplayArea.java:6: error: DisplayArea is not abstract and does not override  
    abstract method actionPerformed(ActionEvent) in ActionListener  
    public class DisplayArea extends JPanel implements ActionListener {  
           ^  
    1 error 

  • Il faut donc implémenter la méthode actionPerformed(ActionEvent e).
  • Pour l'instant, notre implémentation de la méthode actionPerformed n'affichera qu'un message, par exemple «actionPerformed was called».
  • Modifiez le constructeur de la classe GUI, chaque bouton possède une méthode addActionListener, utilisez cette méthode afin d'ajouter l'objet de la classe DisplayArea comme gestionnaire auprès de chacun d'eux.

Vous pouvez maintenant compiler et tester l'application. Chaque fois que l'un des deux boutons est cliqué, le message «actionPerformed was called» sera affiché sur la console.

2.3 paint (Partie 1 de 2)

Chaque fois qu'AWT doit afficher notre objet DisplayArea, il fait un appel à la méthode paint(Graphics g). Modifiez la classe DisplayArea.

  • Vous devrez ajouter une directive import, avec les autres directives import au début du fichier, afin de rendre le nom java.awt.Graphics disponible.
  • Vous devez redéfinir la méthode paint(Graphics g).
  • L'implémentation de la méthode paint fera deux choses :
    • D'abord, elle fera appel à la méthode paint de la superclasse. Pour ce faire, ajoutez l'appel super.paint(g) comme premier énoncé de la méthode paint. Ainsi, tout le travail de la méthode paint héritée de la superclasse JPanel sera fait d'abord, puis il y aura le travail spéficique à notre sous-classe.
    • Ensuite, affichez simplement un message «paint was called».
  • Modifiez la méthode actionPerformed. À la suite de l'appel à la méthode println, ajouter un appel méthode repaint sans arguments.

Compilez et testez l'application.

  • Lorsque l'application sera affichée à l'écran pour la première fois, vous devriez voir le message «paint was called» affiché dans la console.
  • Cliquez sur les boutons. L'appel à la méthode repaint dans la méthode actionPerformed forcera un appel à la méthode paint, vous devriez voir le message «paint was called» affiché dans la console.

2.4 Gestionnaire d'évènement (Partie 2 de 3)

Il y a quatre boutons. Comment la méthode actionPerformed pourra-t-elle de déterminer le bouton qui a été cliqué par l'utilisateur ?

Le paramètre de la méthode actionPerformed est une référence vers un objet de la classe ActionEvent ayant été généré au moment où l'utilisateur a cliqué le bouton.

  • Modifier la méthode actionPerformed. Spécifiquement, concaténez le résultat d'un appel à la méthode e.getActionCommand() au message de la méthode println. Ici, e est le nom du paramètre de la méthode actionPerformed. Voici ma méthode actionPerformed.
    public void actionPerformed(ActionEvent e) { 
        System.out.println("actionPerformed was called: " + e.getActionCommand()); 
        repaint(); 
    }

Avec cette modification, vous devriez obtenir ceci sur la console, si vous cliquez sur les boutons «Right», «Up», «Down», puis «Left».

 java GUI  
paint was called!  
paint was called!  
actionPerformed was called: Right  
paint was called!  
actionPerformed was called: Up  
paint was called!  
actionPerformed was called: Down  
paint was called!  
actionPerformed was called: Left  
paint was called!

Nous utiliserons donc getActionCommand afin de distinguer les différentes situations.

public void actionPerformed(ActionEvent e) { 
 
    String command; 
    command = e.getActionCommand(); 
 
    if (command.equals("Left")) { 
        ; 
    } else if (command.equals("Right")) { 
        ; 
    } else if (command.equals("Up")) { 
        ; 
    } else if (command.equals("Down")) { 
        ; 
    } else { 
        ; 
    } 
    repaint(); 
 
}

2.5 paint (Partie 2 de 2)

Il ne reste plus qu'à dessiner un point sur l'écran et de le déplacer dans la bonne direction chaque fois que l'usager clique sur l'un des boutons.

  • L'objet DispayArea doit mémoriser la position courante du point sur l'écran. Pour ce faire
    • Ajoutez une variable d'instance de type java.awt.Point.
    • Dans le constructeur, initialisez cette variable de sorte que la position initiale du point soit le centre de l'objet DisplayArea.
  • Consultez la documentation de l'objet Graphics pour obtenir la liste des méthodes disponibles.
  • Modifiez la méthode paint :
    • À l'aide d'un appel à la méthode setColor, changez la couleur par défaut pour dessiner sur la surface graphique (l'objet désigné par g).
    • Dessinez un cercle sur la surface graphique (objet désigné par g) à l'aide de sa méthode drawOval. Dessinez le cercle à la position courante (celle de la variable d'instance de type Point).

Votre application devrait maintenant ressembler à ceci :

paint

Figure 2 : paint

2.6 Gestionnaire d'évènement (Partie 3 de 3)

Il suffit maintenant de modifier la méthode actionPerformed afin de le déplacer le point dans l'une des quatre directions, selon bouton que l'utilisateur aura cliqué.

  • Si l'utilisateur clique sur le bouton «Left», déplacez le point de 10 pixels vers la gauche et faites un appel à la méthode repaint. Cet appel à repaint forcera un appel à la méthode paint qui affichera le point à sa nouvelle position.
  • Si l'utilisateur clique sur le bouton «Right», déplacez le point de 10 pixels vers la droite et faites un appel à la méthode repaint. Cet appel à repaint forcera un appel à la méthode paint qui affichera le point à sa nouvelle position.
  • Si l'utilisateur clique sur le bouton «Up», déplacez le point de 10 pixels vers le haut et faites un appel à la méthode repaint. Cet appel à repaint forcera un appel à la méthode paint qui affichera le point à sa nouvelle position.
  • Si l'utilisateur clique sur le bouton «Down», déplacez le point de 10 pixels vers le bas et faites un appel à la méthode repaint. Cet appel à repaint forcera un appel à la méthode paint qui affichera le point à sa nouvelle position.

Compilez et testez votre application.

2.7 JComboBox

On souhaite maintenant ajouter un menu déroulant afin de changer la couleur du point.

paint

Figure 3 : paint

Pour ce faire, nous allons utiliser un objet JComboBox.

  • https://docs.oracle.com/javase/8/docs/api/javax/swing/JComboBox.html
  • Vous devez modifier le constructeur de la classe GUI :

    • Ajouter un objet JComboBox à l'objet JPanel de la zone sud, à la droite du bouton «Down».
      String[]colors;
      colors = new String[] { "red", "black", "blue", "green", "yellow" };
      JComboBox colorList;
      colorList = new JComboBox(colors);

    Notez que JComboBox prend un tableau de String en paramètres. Ces strings seront utilisés pour l’identification des ActionListeners

    • Assurez-vous que l'objet DisplayArea est le gestionnaire d'évènements pour ce menu déroulant.

    Nous devons maintenant modifier la méthode actionPerformed de la classe DisplayArea de sorte à changer la couleur du point en fonction la couleur sélectionnée par l'utilisateur. L'appel e.getActionCommand() retournera la chaîne «comboBoxChanged» lorsque l'utilisateur sélectionne un item du menu déroula.

    • Ajoutez d'abord une nouvelle variable d'instance de type Color à la classe DisplayArea.
    • Modifiez le constructeur afin d'assigner une valeur à la variable d'instance de type Color.
    • Modifiez la méthode paint afin que la couleur du point soit celle de la variable d'instance de type Color.
    • Finalement, ajouter un nouveau cas à la méthode actionPerformed. Lorsque getActionCommand() retoune «comboBoxChanged», obtenez la couleur sélectionnée comme ceci :
      if (e.getSource() instanceof JComboBox) {
      
      	JComboBox cb;
      	cb = (JComboBox) e.getSource();
      	String color;
      	color = (String) cb.getSelectedItem();
      
      	if(color == "red"){
      		currentColor = Color.RED;
      	}
      	//add cases here        

      Ici, currentColor est le nom de ma variable d'instance de type Color. L'un des énoncés est assez compliqué. Regardons chacune de ses parties.

      • La méthode getSource() retourne une référence vers l'objet qui est la source de l'évènement. nous vérifions si il s’agit d’une instance de JComboBox afin de s’assurer du type d’évènement que nous avons reçu. Ainsi, nous pouvons sans danger forcer le type («type cast») comme ceci : (JComboBox) e.getSource()).
      • Nous appelons maintenant la méthode getSelectedItem() et nous forçons son type.
      • Finalement nous regardons quel couleur a été retourné et changeons la variable currentColor en conséquence.
    • Ajouter les cas pour les autres couleurs
    • Maintenant que nous possédons 2 type d’évènement, ajouter une vérification pour vérifier si l’évènement est une instance de JButton. Assurez-vous que cette vérification entoure les bon block de code.

    2.8 Exercices (défis) supplémentaires

    Si le temps le permet, modifiez l'application afin d'enjoliver l'interface et ajouter une fonctionnalité pour choisir la forme du point, soit un carré ou un cercle.

    Sample GUI

    Figure 4 : Sample GUI

    Utlisez les éléments graphiques suivants :

     

Troisième partie

Puzzler

Le jeu Puzzler a une grille de 7 × 7 cellules. Initialement, toutes les cellules sont remplies de billes de cinq couleurs. Lorsque l'utilisateur sélectionne une bille pour la première fois, toutes les billes adjacentes de même couleur sont aussi sélectionnées. Les billes sont alors de couleur grise pour montrer celles qui ont été sélectionnées. Lorsque l'utilisateur clique une deuxième fois l'une des cellules sélectionnées, celles-ci disparaissent. Les billes du haut se déplacent du haut vers le bas et de la gauche vers la droite afin de combler les espaces vides.

Puzzler 1 Puzzler 2 Puzzler 3

Cette implémentation Java comprend trois classes : Puzzler, Board et Cell.

Class Diagrams

Figure 5 : Sample GUI

Vous voyez clairement le rôle de l'héritage et des interfaces pour cette application. Chaque classe est dérivée d'une classe existante. La fenêtre principale est représentée par l'objet Puzzler, une sous-classe de JFrame.

L'objet Board regroupe toutes billes du jeu. Comme il a accès aux billes, c'est aussi le contrôleur (gestionnaire des évènements) de cette application. Cet objet implémente donc la logique du jeu. Cette classe est une sous-classe de la classe JPanel.

Finalement, les tuiles de ce jeu sont réalisées à l'aide d'objets de la classe Cell. La classe Cell est une sous-classe de la classe JButton.

L'une des idées maitresses de cette application est que lorsqu'une cellule disparait, elle affiche seulement une surface blanche. Pour simuler le déplacement des billes du haut vers le bas et de gauche à droite, nous ne faisons que changer le type de la cellule, et donc l'image à afficher. Par exemple, si une bille bleue doit se déplacer de la position (i,j) vers la position (i′,j′). Nous changeons simplement le type de la cellule à la position (i,j) pour représenter une cellule vide, et nous changeons celui de la cellule à la position (i′,j′) pour représenter une bille bleue.

Les diagrammes UML de séquences ci-dessous illustrent certains appels clés de méthodes. Lorsque le constructeur de la classe Puzzler est appelé, il crée un objet Board. Le constructeur de la classe Board, à son tour, créera les instances de la classe Cell. Ce diagramme illustre une création d'objet. Le constructeur de la classe Cell recoit la référence d'un objet de la classe Board, qu'il utilisera comme gestionnaire d'évènement (appel à addActionListener(b)).

La seconde partie du diagramme illustre une séquence d'actions résultant d'un clique de souris. Lorsque l'utilisateur clique sur le bouton, un objet de la classe ActionEvent est créé, la méthode processEvent du bouton est appelée avec la référence de l'objet ActionEvent comme paramètre. Le bouton à son tour fera un appel à la méthode actionPerformed de son gestionnaire d'évévenement (c'est-à-dire l'objet qui s'est enregistré à l'aide de la méthode addActionListener). L'objet Board déterminera la source de l'évènement. Il interagira avec la cellule afin de déterminer son type et sa position sur la grille. Si la cellule n'était pas sélectionnée, alors le gestionnaire (objet Board) sélectionne toutes les cellules adjacentes de même type (couleur). Éventuellement, le contrôle revient à l'appelant.

Puzzler Sequence

Figure 5 : Puzzler Sequence

Le premier lien vous permet de télécharger l'application, alors que le second est une application que vous pouvez double-cliquer pour la lancer.

Le jeu Puzzler est implémentation Java d'une application JavaScript développée par Apple (Puzzler, Puzzler.zip).

 

Quatrième partie

Une des compétences les plus importantes à développer sont les compétences de débogage. Autrement, la programmation est un exercice de frustration et une perte de temps.

Quiz (1 point)

Le programme suivant contient exactement trois (3) erreurs. Veuillez les identifier.

Votre aide à l'enseignement vous communiquera ses exigences pour la remise de votre réponse à cette question.

Resources

 

Table of Contents