CONSTRUIRE UNE
IHM
Introduction
On désigne sous le nom d’interface, ou bien IHM (Interface homme-machine) la partie visible à l’écran d’une application. L’interface est le 1er contact qu’a un utilisateur avec une application. C’est elle qui détermine la première impression des utilisateurs : il est donc important de lui accorder un intérêt tout particulier.
Pour développer des IHM, nous allons étudier la librairie Java : Swing (package), descendante de la librairie AWT (utilisée pour créer des applets si le client n’a pas le plugin Java correspondant)
Différence majeur entre AWT et SWING :
AWT : L’apparence des composants graphiques dépend du système d’exploitation car elle utilise les ressources systèmes. SWING : Les éléments graphiques développés à l’aide SWING sont dessinées par la librairie elle-même : l’affichage n’est pas délégué au système : cette architecture est plus souple mais plus consommatrice en temps car les calculs sont exécutés par la machine virtuelle Java et non par le système d’exploitation !
Les classes composant la librairie SWING sont des classes dérivées des classes composant AWT ! Exemple : la classe JFrame (de Swing) est une classe dérivée de la classe Frame (d’AWT). Pour les différencier, il suffit d’observer la 1ère
lettre ! |
|
Concepts
fondamentaux
Les composants :
Les gestionnaires de mise en forme : ils sont associés à un conteneur : ils servent à gérer la disposition des composants au sein d’un même conteneur.
La conception d’une interface graphique en Java ressemble un peu à un jeu de construction. On imbrique des composants simples dans des conteneurs qui peuvent être eux mêmes imbriqués dans d’autres conteneurs !
Vous concevez un logiciel pour un utilisateur type, essayez donc de comprendre ces préoccupations.
Phases de création d'une interface
:
1. Réflexion sur l'interface : profitez de votre expérience d'utilisateur de logiciels, faites des interfaces assez semblables … Faites participer le ou les futurs utilisateurs
2. Création : sur papier des différents écrans
3. Test : par des futurs utilisateurs !
4. Ecriture du code…..
Règle : constuire une Interface normalisée
Pour cela, utiliser toujours
les mêmes repères :
Quelques règles en vrac
Ø Un écran se lit de bas en haut et de gauche à droite. Positionnez donc les informations importantes dans le coin supérieur gauche de l'écran. Alignez les données verticalement et horizontalement !
Ø Les boutons de commande doivent être soit centrés le long du bord inférieur de l'écran, soit empilés dans le coin supérieur droit ou inférieur droit. Limitez le nombre de boutons. Utilisez une taille identique pour les boutons de commande.
Ø Un seul style de police peut suffire.
Ø Affecter des touches d'accès rapide à chacun des éléments de l'interface (au cas où la souris ne fonctionne pas ou mal (portables), ou bien pour les utilisateurs qui vont plus vite au clavier).
Ø Demander confirmation à l'utilisateur pour toute opération destructrice.
Ø Désactiver temporairement les contrôles qui n'ont pas de raison d'être utilisés....
Choix
du type de fenêtre
Dans ce cas, vous utiliserez :
· Soit la classe JFrame : elle permet la création de fenêtres principales. Rem : Seule une fenêtre de classe JFrame peut comporter un menu !
· Soit la classe JDialog : elle permet la création de fenêtres secondaires ou boites de dialogue. Rem : Seule une fenêtre de classe JDialog peut être modale ! Une fenêtre B est modale par rapport à une fenêtre A si l’affichage de B empêche l’accès et non pas la vue à la fenêtre A.
· Soit la classe JInternalFrame : elle permet la création de fenêtres internes à une fenêtre de classe JFrame.
Mais toutes les fenêtres ont des propriétés communes. Elles ont toutes :
· un contenu que l’on récupère grâce à la méthode : Container getContentPane()
· une taille par défaut : 0 * 0 que l’on peut modifier avec setSize ou setBounds
· un titre que l’on peut modifier avec setTitle ( String title)
·
un comportement par défaut : lors de sa
fermeture,l’action est de type HIDE_ON_CLOSE
que l’on peut modifier à l’aide de
la méthode
setDefaultCloseOperation(int operation)
· ….
classe : JOptionPane
avec les méthodes statiques suivantes :
classe : JFileChooser
avec les méthodes non statiques suivantes :
· int showOpenDialog(Component parent)
· int showSaveDialog(Component parent)
et les constructeurs suivants :
·
JFileChooser(File currentDirectory)
· JFileChooser()
classe : JColorChooser
avec la méthode statique suivante :
·
Color showDialog(Component component,
String title, Color initialColor)
Les
étapes lors de la définition d’une classe IHM
C’est définir une nouvelle classe héritant d’une classe Java.
|
( étape préalable : récupérer
le contenu de la fenêtre)
( étape préalable : supprimer
tout gestionnaire de mis en forme automatique) |
1 2 3 4 5 6 |
import javax.swing.* ; import java.awt.* ; class MaFenetre extends JDialog { private
JLabel labNom ; private
JTextField txtNom ; private Container contenu ; public MaFenetre( ) { super();
// initialise les attributs d’une JDialog this.labNom = new JLabel
(“nom”); this.txtNom = new
JTextField (“nom”); this.contenu =
this.getContentPane( ) ; this.contenu.add (labNom
) ; this.contenu.add (txtNom
) ; this.contenu.setLayout(null); this.labNom.setBounds( 20, 20 ,
80, 20 ) ; this.txtNom .setBounds(
100 , 20 , 80 , 20) ; } } |
C’est créer un objet. Exemple :
Class TestIHM { public static void main ( String []
args) { MaFenetre maf= new
MaFenetre(); maf.setBounds(20,20,300,300); maf.setVisible(true); } } |
Tour
d’horizon des composants simples
Cette classe permet de créer des « labels » : un label permet d’afficher du texte statique et/ou une image. Ce composant ne réagit pas aux interactions de l’utilisateur.
Avec les constructeurs :
· JLabel()
· JLabel(String txt)
· JLabel(Icon image)
Rem : Icon est une interface. ImageIcon est une classe implémentant Icon.
·
ImageIcon(String
path)
Ces classes permettent de créer des objets destinés à la saisie utilisateur.
Elles ont toutes un constructeur par défaut et des méthodes bien utiles :
· String getText() : permet de récupérer le texte saisi par l’utilisateur
· void setText(String txt) : permet de modifier par programmation le contenu d’un champ
Cette classe permet de créer des boutons simples, ils peuvent contenir du texte et et/ou une image. Avec les constructeurs :
·
JButton(String
txt)
·
JButton(Icon
img),...
Ces classes permettent de créer respectivement des cases à cocher et des boutons radio . Avec les constructeurs suivants :
· JCheckBox(Icon icon)
·
JCheckBox(String
txt)
·
JRadioButton(String
txt),...
A la seule différence que les boutons radios doivent être regroupés au sein d’un groupe afin qu’un seul par groupe puisse être sélectionné. Il faut alors utiliser la classe ButtonGroup. Avec le constructeur suivant :
· ButtonGroup()
Et la méthode d’ajout de bouton à un groupe :
·
void
add(AbstractButton b)
Cette classe permet de créer des listes déroulantes. Avec les constructeurs suivants :
· JComboBox()
· JComboBox(Vector list)
Et des méthodes bien utiles :
·
Object
getSelectedItem()
·
int
getSelectedIndex()
·
void
addItem(Object anObject)
Rem : Vector est une classe avec un constructeur par défaut et une méthode bien utile :
·
boolean
add(Object o)
Cette classe permet de créer des tables. Remarque : une table peut être créée indépendamment de tout autre composant, cependant, vous avez tout intérêt à l’associer à un conteneur JScrollPane. Exemple : ci dessous :
Avec les constructeurs suivants :
·
JTable(Object[][] rowData,
Object[] columnNames)
·
JTable()
Tour
d’horizon des conteneurs autres que les fenêtres
La classe JPanel est très utilisée, elle permet la création de conteneurs dont l’aspect est réduit au strict minimum. C’est une sorte de rectangle qui peut être invisible !
Munie d’un constructeur par défaut.
Avec les constructeurs suivants :
·
JScrollPane()
·
JScrollPane(Component view)
Munie d’un constructeur par défaut. Et d’une méthode bien utile :
·
void
addTab(String title, Component component)
Cette classe sert à créer des fenêtres internes. Mais pour cela, il faut :
1. Créer un autre conteneur de classe JDesktopPane appelé bureau.
Avec le constructeur JDesktopPane()
2. L’ajouter à un objet de type JFrame.
3. Créer une JInternalFrame
4. L’ajouter au bureau
Propriétés
communes à tous les composants Swing
Beaucoup de propriétés sont communes à tous les
composants car ce sont des propriétés définies au niveau de la super classe Component
ou JComponent, donc toutes ses classes filles en héritent ! La
liste qui suit est loin d’être exhaustive, si vous voulez en savoir plus sur
les propriétés communes vous devez alors consulter la documentation sur les
classes mères !
A l’aide des méthodes suivantes, vous pouvez positionner et tailler les composants :
·
void
setLocation(int x, int y)
·
vois
setSize(int width, int height)
·
void
setBounds(intx, int y, int width, int height)
Un composant peut être actif ou inactif. Un composant inactif est inaccessible pour l’utilisateur. Un composant peut être visible ou non visible. A l’aide des méthodes suivantes :
·
void
setEnabled(boolean b)
·
void
setVisible(boolean b)
La couleur des composants peut être paramétrée grâce aux méthodes :
·
setBackGround
(Color bg)
·
setForeGroung
(Color fg)
Il est possible d’associer une bordure à chaque composant à l’aide de la méthode :
· void setBorder(Border border)
Rem : Border n’est qu’une interface. Il faut alors utiliser les classes suivantes :
LineBorder, MetalBorders.ButtonBorder,….
Créer
des menus
|
|
|
|
|
|
Méthodes
|
|
|
|
|
|
|
|
|
|
Méthodes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setJMenuBar(JMenuBar mb) Sets the menu bar for this frame to the
specified menu bar. |
Gestion des évènements
1er
exemple : Rendre un bouton interactif
Ce 1er exemple traitera de l’événement le plus courant : l’événement Action : celui-ci correspond au clic de souris sur un bouton. Pour qu’il y ait gestion d’événements, il doit y avoir un écouteur d’événement, celui-ci définit les traitements à éxecuter en cas d’événement.
Etape préalable : rappel des librairies gérant les
événements
ActionListener oblige les classes voulant être ActionListener à définir la méthode actionPerformed.
|
Exemple :
2 1 3 4 |
import java.awt.event.*; public class MaFenetre extends JDialog implements ActionListener { private JButton butOk ; private Container contenu ; public MaFenetre( ) { super(); this.butOk = new JButton
(“OK”); this.butOk.addActionListener
(this); this.contenu = this.getContentPane( ) ; this.contenu.add (butOK
) ; this.contenu.setLayout(null); this.butOK.setBounds( 20, 20 ,
80, 20 ) ; } public void actionPerformed( ActionEvent
evt) { if (evt.getSource() == butOK )
JOptionPane.showMessageDialog(this, “OK”); } } |
Qu’est
ce qu’un événement ?
Toutes les interactions de l’utilisateur ( déplacement de la souris, clic sur un bouton, …) donnent naissance à des évènements. Un événement est représenté par un objet. Ces objets contiennent des informations concernant leur contexte d’apparition ( ex : composant graphique source de l’événement à l’aide de la méthode getSource() ) Mais, ils diffèrent selon la nature de l’événement :
Interaction utilisateur => donne naissance à un Objet de classe |
|
Clic sur un composant simple ( ex : bouton ) |
ActionEvent |
Clic sur un conteneur ( ex : JPanel ) |
MouseEvent |
Sélection ou désélection d’un item |
ItemEvent |
Fermeture, agrandissement, maximisation de la fenêtre |
WindowEvent |
.... |
… |
Et selon leur nature, on peut avoir diverses informations. Par exemple pour un ActionEvent, on peut connaître l’instant de l’événement. Pour un MouseEvent, on peut connaître le nombre de clics, le bouton cliqué,….
Toutes ces classes héritent de la classe EventObject.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Remarque : Le programmeur n’a pas à se soucier de la création des évènements. Mais par contre, il doit les intercepter afin pouvoir les traiter.
Comment
réagir à un événement ?
Pour intercepter des événements, il faut utiliser des objets appelés « listeners ». Ce sont des objets à l’écoute d’un événement précis.
Un Objet « événement » de classe peut être écouté par un objet « listener » de classe |
|
ActionEvent |
ActionListener
|
MouseEvent |
Mouse
Listener |
ItemEvent |
ItemListener |
WindowEvent |
WindowListener |
.... |
… |
N’importe quel objet peut être « listener ». Généralement, on choisit pour écouteur la fenêtre, ainsi la fenêtre gère les événements qui peuvent avoir lieu à l’intérieur d’elle-même.
Remarque : ActionListener,… ne sont pas réellement des classes, ce sont des interfaces !
Définition : Une interface est une classe particulière, elle ne définit aucune variable d’instance et toutes ses méthodes sont abstraites. Une interface ne fait que représenter un ensemble de comportements, sans les implémenter.
Conséquence : Pour qu’une fenêtre soit « listener » d’événement de type ActionEvent, on dit qu’elle implémente l’interface ActionListener ! Ce qui implique qu’elle doit implémenter les comportements exigés par cette interface ( Soit dans ce cas : actionPerformed())
Mais ce statut d’écouteur n’est pas automatique, le
listener doit s’abonner auprès du composant qui génère l’évènement. En
s’abonnant, il demande ainsi au composant de le prévenir en cas d’événement.
Cet abonnement se fait à l’aide des méthodes : add….Listener.
Interface ActionListener
|
|
|
|
Interface MouseListener
|
|
|
|
|
|
|
|
|
|
|
|
Interface ItemListener
|
|
|
|
Interface WindowListener
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Les
différentes méthodes pour implémenter les listeners
Il existe différentes méthodes pour implémenter les listener :
Exemple classe
indépendante |
|
|
Exemple classe interne anonyme |
|
Remarque
: new WindowAdapter() {…} renvoie la
référence d’un objet dont la classe est anonyme
et
dérive de WindowAdapter .Toutefois, Sun précise qu’il est important de
ne pas abuser de ces classes et d’en limiter le nombre d’instructions afin de
ne pas rendre le code illisible.
Les layouts ou
Les gestionnaires de mise en forme
Pourquoi déléguer la gestion de la position des composants à
un layout, alors que le positionnement semble si simple ? Mais que se
passe t’il si j’agrandis ma fenêtre ? Les composants doivent-ils
s’agrandir ? Sinon, où ajoute t’on de l’espace ? Les layouts
implémentent des stratégies de positionnement !
Par défaut, ce layout est associé
aux conteneurs excepté à la frame.
Une stratégie simple et très utile est un placement des éléments graphique les uns à cotés des autres, de gauche à droite ! Lorsqu’il n’y a plus de place dans la fenêtre, les composants sont mis automatiquement en dessous !
Les composants peuvent être alignés à gauche, à droite ou centrés. Pour définir cela, il suffit de passer au constructeur du flowLayout une des valeurs suivantes : FlowLayout.LEFT, FlowLayout.RIGHT, FlowLayout.CENTER
|
|
Exemple : FlowLayout
monLayout = new FlowLayout(FlowLayout.CENTER) ; JPanel
monPanel = new JPanel(); monPanel.setLayout(monLayout)
; ou monPanel.setLayout(
new FlowLayout(FlowLayout.CENTER) ) ; |
Le gridLayout implémente une stratégie simple et
efficace. L’espace du conteneur est découpé en une grille Le nombre de colonnes
et de lignes composant cette grille est fixé lors de l’instanciation du
layout..
Toutes les cases et donc les composants contenus dedans
auront donc la même taille.
|
|
Exemple : FlowLayout
monLayout = new GridLayout(3,2) ; JPanel
monPanel = new JPanel(); monPanel.setLayout(monLayout)
; ou monPanel.setLayout(
new GridLayout(3,2) ) ; |
Le BoxLayout
permet d’aligner les composants les uns à côtés des autres, ou les uns
en dessous des autres.
|
Avec axis={BoxLayout.X_AXIS,
BoxLayout.Y_AXIS}
Exemple : JPanel
monPanel = new JPanel(); BoxLayout
monLayout = new BoxLayout(monPanel, X_AXIS) ; monPanel.setLayout(monLayout)
; |
Par défaut, ce layout est
associé aux conteneurs de type frame.
Le borderLayout implémente une stratégie de positionnement tout à fait particulière. Il réserve dans l’espace de son conteneur 5 cases : case centrale, case nord, case sud, case ouest, case est. On peut mettre q’un seul composant dans une case ! Lorsque l’on utilise ce type de Layout, il faut alors indiquer dans quelle zone on désire ajouter les autres composants à l’aide des varaiables suivantes : BorderLayout.CENTER, BorderLayout.WEST, BorderLayout.NORTH
|
|
Exemple : BoxLayout
monLayout = new BoderLayout(); JPanel
monPanel = new JPanel(); monPanel.setLayout(monLayout)
; monPanel.add(new
Jbutton(“OK”),BorderLayout.CENTER); |
GridBagLayout représente le gestionnaire de placement le plus complexe et le plus flexible de la plateforme Java.
GridBagLayout aligne les composants en les plaçant dans une grille composée de colonnes et de lignes. Chaque cellule peut contenir un à plusieurs composants. D'autre part, contrairement à GridLayout, il est possible que les composants soient placés sur une ou plusieurs cellules. De même, les lignes et les colonnes dans la grille ne sont pas nécessairement toutes de la même hauteur et de la même largeur.
La gestion du placement des composants nécessitent l'instanciation de la classe GridBagConstraints chargée d'exposer les contraintes de positionnement.
Exemple :
Container panneauContenu = getContentPane();
GridBagLayout grille = new GridBagLayout();
GridBagConstraints contraintes = new GridBagConstraints();
contentPane.setLayout(grille);
JTextArea zoneTextuelle = new JTextArea("Une zone de texte");
contraintes.gridwidth = 3;
contraintes.gridheight = 5;
contraintes.gridx = 0;
contraintes.gridy = 1;
grille.setConstraints(zoneTextuelle, contraintes);
panneauContenu.add(zoneTextuelle);