Notre première fenêtre

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Dans cette partie, nous aborderons les interfaces graphiques (on parle aussi d'IHM pour Interfaces Homme Machine ou de GUI pour Graphical User Interfaces) et, par extension, la programmation événementielle. Par là, vous devez comprendre que votre programme ne réagira plus à des saisies au clavier mais à des événements provenant d'un composant graphique : un bouton, une liste, un menu…

Le langage Java propose différentes bibliothèques pour programmer des IHM, mais dans cet ouvrage, nous utiliserons essentiellement les packages javax.swing et java.awt présents d'office dans Java. Ce chapitre vous permettra d'apprendre à utiliser l'objet JFrame, présent dans le package java.swing. Vous serez alors à même de créer une fenêtre, de définir sa taille, etc.

Le fonctionnement de base des IHM vous sera également présenté et vous apprendrez qu'en réalité, une fenêtre n'est qu'une multitude de composants posés les uns sur les autres et que chacun possède un rôle qui lui est propre. Mais trêve de bavardages inutiles, commençons tout de suite !

L'objet JFrame

Avant de nous lancer à corps perdu dans cette partie, vous devez savoir de quoi nous allons nous servir. Dans ce cours, nous traiterons de javax.swing et de java.awt. Nous n'utiliserons pas de composants awt, nous travaillerons uniquement avec des composants swing ; en revanche, des objets issus du package awt seront utilisés afin d'interagir et de communiquer avec les composants swing. Par exemple, un composant peut être représenté par un bouton, une zone de texte, une case à cocher, etc.

Afin de mieux comprendre comment tout cela fonctionne, vous devez savoir que lorsque le langage Java a vu le jour, dans sa version 1.0, seul awt était utilisable ; swing n'existait pas, il est apparu dans la version 1.2 de Java (appelée aussi Java 2). Les composants awt sont considérés comme lourds (on dit aussi HeavyWeight) car ils sont fortement liés au système d'exploitation, c'est ce dernier qui les gère. Les composants swing, eux, sont comme dessinés dans un conteneur, ils sont dit légers (on dit aussi LightWeight) ; ils n'ont pas le même rendu à l'affichage, car ce n'est plus le système d'exploitation qui les gère. Il existe également d'autres différences, comme le nombre de composants utilisables, la gestion des bordures…

Pour toutes ces raisons, il est très fortement recommandé de ne pas mélanger les composants swing et awt dans une même fenêtre ; cela pourrait occasionner des conflits ! Si vous associez les deux, vous aurez de très grandes difficultés à développer une IHM stable et valide. En effet, swing et awt ont les mêmes fondements mais diffèrent dans leur utilisation.

Cette parenthèse fermée, nous pouvons entrer dans le vif du sujet. Je ne vous demande pas de créer un projet contenant une classe main, celui-ci doit être prêt depuis des lustres ! Pour utiliser une fenêtre de type JFrame, vous devez l'instancier, comme ceci :

1
2
3
4
5
6
7
import javax.swing.JFrame;

public class Test {
  public static void main(String[] args){       
    JFrame fenetre = new JFrame();
  }       
}

Lorsque vous exécutez ce code, vous n'obtenez rien, car par défaut, votre JFrame n'est pas visible. Vous devez donc lui dire « sois visible » de cette manière :

1
2
3
4
5
6
7
8
import javax.swing.JFrame;

public class Test {
  public static void main(String[] args){
    JFrame fenetre = new JFrame();
    fenetre.setVisible(true);
  }       
}

Ainsi, lorsque vous exécutez ce code, vous obtenez la figure suivante.

Première fenêtre

À toutes celles et ceux qui se disent que cette fenêtre est toute petite, je réponds : « Bienvenue dans le monde de la programmation événementielle ! » Il faut que vous vous y fassiez, vos composants ne sont pas intelligents : il va falloir leur dire tout ce qu'ils doivent faire.

Pour obtenir une fenêtre plus conséquente, il faudrait donc :

  • qu'elle soit plus grande ;
  • qu'elle comporte un titre (ce ne serait pas du luxe !) ;
  • qu'elle figure au centre de l'écran, ce serait parfait ;
  • que notre programme s'arrête réellement lorsqu'on clique sur la croix rouge, car, pour ceux qui ne l'auraient pas remarqué, le processus Eclipse tourne encore même après la fermeture de la fenêtre.

Pour chacun des éléments que je viens d'énumérer, il y a aura une méthode à appeler afin que notre JFrame sache à quoi s'en tenir. Voici d'ailleurs un code répondant à toutes nos exigences :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import javax.swing.JFrame;

public class Test {
  public static void main(String[] args){

    JFrame fenetre = new JFrame();

    //Définit un titre pour notre fenêtre
    fenetre.setTitle("Ma première fenêtre Java");
    //Définit sa taille : 400 pixels de large et 100 pixels de haut
    fenetre.setSize(400, 100);
    //Nous demandons maintenant à notre objet de se positionner au centre
    fenetre.setLocationRelativeTo(null);
    //Termine le processus lorsqu'on clique sur la croix rouge
    fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //Et enfin, la rendre visible        
    fenetre.setVisible(true);
  }       
}

Voyez le rendu de ce code en figure suivante.

Une fenêtre plus adaptée

Afin de ne pas avoir à redéfinir les attributs à chaque fois, je pense qu'il serait utile que nous possédions notre propre objet. Comme ça, nous aurons notre propre classe !

Pour commencer, effaçons tout le code que nous avons écrit dans notre méthode main. Créons ensuite une classe que nous allons appeler Fenetre et faisons-la hériter de JFrame. Nous allons maintenant créer notre constructeur, dans lequel nous placerons nos instructions. Cela nous donne :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import javax.swing.JFrame;

public class Fenetre extends JFrame {
  public Fenetre(){
    this.setTitle("Ma première fenêtre Java");
    this.setSize(400, 500);
    this.setLocationRelativeTo(null);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);             
    this.setVisible(true);
  }
}

Ensuite, vous avez le choix : soit vous conservez votre classe contenant la méthode main et vous créez une instance de Fenetre, soit vous effacez cette classe et vous placez votre méthode main dans votre classe Fenetre. Mais dans tous les cas, vous devez créer une instance de votre Fenetre. Personnellement, je préfère placer ma méthode main dans une classe à part… Mais je ne vous oblige pas à faire comme moi ! Quel que soit l'emplacement de votre main, la ligne de code suivante doit y figurer :

1
Fenetre fen = new Fenetre();

Exécutez votre nouveau code, et… vous obtenez exactement la même chose que précédemment. Vous conviendrez que c'est tout de même plus pratique de ne plus écrire les mêmes instructions à chaque fois. Ainsi, vous possédez une classe qui va se charger de l'affichage de votre futur programme. Et voici une petite liste de méthodes que vous serez susceptibles d'utiliser.

Positionner la fenêtre à l'écran

Nous avons déjà centré notre fenêtre, mais vous voudriez peut-être la positionner ailleurs. Pour cela, vous pouvez utiliser la méthode setLocation(int x, int y). Grâce à cette méthode, vous pouvez spécifier où doit se situer votre fenêtre sur l'écran. Les coordonnées, exprimées en pixels, sont basées sur un repère dont l'origine est représentée par le coin supérieur gauche (figure suivante).

Coordonnées sur votre écran

La première valeur de la méthode vous positionne sur l'axe x, 0 correspondant à l'origine ; les valeurs positives déplacent la fenêtre vers la droite tandis que les négatives la font sortir de l'écran par la gauche. La même règle s'applique aux valeurs de l'axe y, si ce n'est que les valeurs positives font descendre la fenêtre depuis l'origine tandis que les négatives la font sortir par le haut de l'écran.

Empêcher le redimensionnement de la fenêtre

Pour cela, il suffit d'invoquer la méthode setResizable(boolean b) : false empêche le redimensionnement tandis que true l'autorise.

Garder la fenêtre au premier plan

Il s'agit là encore d'une méthode qui prend un booléen en paramètre. Passer true laissera la fenêtre au premier plan quoi qu'il advienne, false annulera cela. Cette méthode est setAlwaysOnTop(boolean b).

Retirer les contours et les boutons de contrôle

Pour ce faire, il faut utiliser la méthode setUndecorated(boolean b).

Je ne vais pas faire le tour de toutes les méthodes maintenant, car de toute façon, nous allons nous servir de bon nombre d'entre elles très prochainement.Cependant, je suppose que vous aimeriez bien remplir un peu votre fenêtre. Je m'en doutais, mais avant il vous faut encore apprendre une bricole. En effet, votre fenêtre, telle qu'elle apparaît, vous cache quelques petites choses !

Vous pensez, et c'est légitime, que votre fenêtre est toute simple, dépourvue de tout composant (hormis les contours). Eh bien vous vous trompez ! Une JFrame est découpée en plusieurs parties superposées, comme le montre la figure suivante.

Structure d'une JFrame

Nous avons, dans l'ordre :

  • la fenêtre ;
  • le RootPane (en vert), le conteneur principal qui contient les autres composants ;
  • le LayeredPane (en violet), qui forme juste un panneau composé du conteneur global et de la barre de menu (MenuBar) ;
  • la MenuBar (en orange), la barre de menu, quand il y en a une ;
  • le content pane (en rose) : c'est dans celui-ci que nous placerons nos composants ;
  • le GlassPane (en transparence), couche utilisée pour intercepter les actions de l'utilisateur avant qu'elles ne parviennent aux composants.

Pas de panique, nous allons nous servir uniquement du content pane. Pour le récupérer, il nous suffit d'utiliser la méthode getContentPane() de la classe JFrame. Cependant, nous allons utiliser un composant autre que le content pane : un JPanel dans lequel nous insérerons nos composants.

Il existe d'autres types de fenêtre : la JWindow, une JFrame sans bordure et non draggable (déplaçable), et la JDialog, une fenêtre non redimensionnable. Nous n'en parlerons toutefois pas ici.

L'objet JPanel

Comme je vous l'ai dit, nous allons utiliser un JPanel, composant de type conteneur dont la vocation est d'accueillir d'autres objets de même type ou des objets de type composant (boutons, cases à cocher…).

Voici le marche à suivre :

  1. Importer la classe javax.swing.JPanel dans notre classe héritée de JFrame.
  2. Instancier un JPanel puis lui spécifier une couleur de fond pour mieux le distinguer.
  3. Avertir notre JFrame que ce sera notre JPanel qui constituera son content pane.

Rien de bien sorcier, en somme. Qu'attendons-nous ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.awt.Color; 
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Fenetre extends JFrame {
  public Fenetre(){             
    this.setTitle("Ma première fenêtre Java");
    this.setSize(400, 100);
    this.setLocationRelativeTo(null);               

    //Instanciation d'un objet JPanel
    JPanel pan = new JPanel();
    //Définition de sa couleur de fond
    pan.setBackground(Color.ORANGE);        
    //On prévient notre JFrame que notre JPanel sera son content pane
    this.setContentPane(pan);               
    this.setVisible(true);
  }       
}

Vous pouvez voir le résultat à la figure suivante.

Premier JPanel

C'est un bon début, mais je vois que vous êtes frustrés car il n'y a pas beaucoup de changement par rapport à la dernière fois. Eh bien, c'est maintenant que les choses deviennent intéressantes ! Avant de vous faire utiliser des composants (des boutons, par exemple), nous allons nous amuser avec notre JPanel. Plus particulièrement avec un objet dont le rôle est de dessiner et de peindre notre composant. Ça vous tente ? Alors, allons-y !

Les objets Graphics et Graphics2D

L'objet Graphics

Nous allons commencer par l'objet Graphics.Cet objet a une particularité de taille : vous ne pouvez l'utiliser que si et seulement si le système vous l'a donné via la méthode getGraphics() d'un composant swing ! Pour bien comprendre le fonctionnement de nos futurs conteneurs (ou composants), nous allons créer une classe héritée de JPanel : appelons-la Panneau. Nous allons faire un petit tour d'horizon du fonctionnement de cette classe, dont voici le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel { 
  public void paintComponent(Graphics g){
    //Vous verrez cette phrase chaque fois que la méthode sera invoquée
    System.out.println("Je suis exécutée !"); 
    g.fillOval(20, 20, 75, 75);
  }               
}

Qu'est-ce que c'est que cette méthode ?

Cette méthode est celle que l'objet appelle pour se dessiner sur votre fenêtre ; si vous réduisez cette dernière et que vous l'affichez de nouveau, c'est encore cette méthode qui est appelée pour afficher votre composant. Idem si vous redimensionnez votre fenêtre… De plus, nous n'avons même pas besoin de redéfinir un constructeur car cette méthode est appelée automatiquement !

C'est très pratique pour personnaliser des composants, car vous n'aurez jamais à l'appeler vous-mêmes : c'est automatique ! Tout ce que vous pouvez faire, c'est forcer l'objet à se repeindre ; ce n'est toutefois pas cette méthode que vous invoquerez, mais nous y reviendrons.

Vous aurez constaté que cette méthode possède un argument et qu'il s'agit du fameux objet Graphics tant convoité. Nous reviendrons sur l'instruction g.fillOval(20, 20, 75, 75), mais vous verrez à quoi elle sert lorsque vous exécuterez votre programme.

Voici maintenant notre classe Fenetre :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import javax.swing.JFrame;

public class Fenetre extends JFrame {
  public Fenetre(){                
    this.setTitle("Ma première fenêtre Java");
    this.setSize(100, 150);
    this.setLocationRelativeTo(null);               
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setContentPane(new Panneau());

    this.setVisible(true);
  }     
}

Exécutez votre main, vous devriez obtenir la même chose qu'à la figure suivante.

Test de l'objet Graphics

Une fois votre fenêtre affichée, étirez-la, réduisez-la… À présent, vous pouvez voir ce qu'il se passe lorsque vous interagissez avec votre fenêtre : celle-ci met à jour ses composants à chaque changement d'état ou de statut. L'intérêt de disposer d'une classe héritée d'un conteneur ou d'un composant, c'est que nous pouvons redéfinir la façon dont est peint ce composant sur la fenêtre.

Après cette mise en bouche, explorons un peu plus les capacités de notre objet Graphics. Comme vous avez pu le voir, ce dernier permet, entre autres, de tracer des ronds ; mais il possède tout un tas de méthodes plus pratiques et amusantes les unes que les autres… Nous ne les étudierons pas toutes, mais vous aurez déjà de quoi faire.

Pour commencer, reprenons la méthode utilisée précédemment : g.fillOval(20, 20, 75, 75). Si nous devions traduire cette instruction en français, cela donnerait : « Trace un rond plein en commençant à dessiner sur l'axe x à 20 pixels et sur l'axe y à 20 pixels, et fais en sorte qu'il occupe 75 pixels de large et 75 pixels de haut. »

Oui, mais si je veux que mon rond soit centré et qu'il le reste ?

C'est dans ce genre de cas qu'il est intéressant d'utiliser une classe héritée. Puisque nous sommes dans notre objet JPanel, nous avons accès à ses données lorsque nous le dessinons.

En effet, il existe des méthodes dans les objets composants qui retournent leur largeur (getWidth()) et leur hauteur (getHeight()). En revanche, réussir à centrer un rond dans un JPanel en toutes circonstances demande un peu de calcul mathématique de base, une pincée de connaissances et un soupçon de logique !

Reprenons notre fenêtre telle qu'elle se trouve en ce moment. Vous pouvez constater que les coordonnées de départ correspondent au coin supérieur gauche du carré qui entoure ce cercle, comme le montre la figure suivante.

Point de départ du cercle dessiné

Cela signifie que si nous voulons que notre cercle soit tout le temps centré, il faut que notre carré soit centré, donc que le centre de celui-ci corresponde au centre de notre fenêtre ! La figure suivante est un schéma représentant ce que nous devons obtenir.

Coordonnées recherchées

Ainsi, le principe est d'utiliser la largeur et la hauteur de notre composant ainsi que la largeur et la hauteur du carré qui englobe notre rond ; c'est facile, jusqu'à présent…

Maintenant, pour trouver où se situe le point depuis lequel doit commencer le dessin, il faut soustraire la moitié de la largeur du composant à la moitié de celle du rond afin d'obtenir la valeur sur l'axe x, et faire de même (en soustrayant les hauteurs, cette fois) pour l'axe y. Afin que notre rond soit le plus optimisé possible, nous allons donner comme taille à notre carré la moitié de la taille de notre fenêtre ; ce qui revient, au final, à diviser la largeur et la hauteur de cette dernière par quatre. Voici le code correspondant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel { 
  public void paintComponent(Graphics g){                
    int x1 = this.getWidth()/4;
    int y1 = this.getHeight()/4;                      
    g.fillOval(x1, y1, this.getWidth()/2, this.getHeight()/2);
  }       
}

Si vous testez à nouveau notre code, vous vous apercevez que notre rond est maintenant centré. Cependant, l'objet Graphics permet d'effectuer plein d'autres choses, comme peindre des ronds vides, par exemple. Sans rire ! Maintenant que vous avez vu comment fonctionne cet objet, nous allons pouvoir utiliser ses méthodes.

La méthode drawOval()

Il s'agit de la méthode qui permet de dessiner un rond vide. Elle fonctionne exactement de la même manière que la méthode fillOval(). Voici un code mettant en œuvre cette méthode :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){                
    int x1 = this.getWidth()/4;
    int y1 = this.getHeight()/4;
    g.drawOval(x1, y1, this.getWidth()/2, this.getHeight()/2);
  }               
}

Le résultat se trouve en figure suivante.

Rendu de la méthode drawOval()

Si vous spécifiez une largeur différente de la hauteur, ces méthodes dessineront une forme ovale.

La méthode drawRect()

Cette méthode permet de dessiner des rectangles vides. Bien sûr, son homologue fillRect() existe. Ces deux méthodes fonctionnent de la même manière que les précédentes, voyez plutôt ce code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){
    //x1, y1, width, height
    g.drawRect(10, 10, 50, 60);
    g.fillRect(65, 65, 30, 40);
  }               
}

Le résultat se trouve à la figure suivante.

Rendu des méthodes drawRect() et fillRect()

La méthode drawRoundRect()

Il s'agit du même élément que précédemment, hormis le fait que le rectangle sera arrondi. L'arrondi est défini par la valeur des deux derniers paramètres.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel { 
  public void paintComponent(Graphics g){
    //x1, y1, width, height, arcWidth, arcHeight
    g.drawRoundRect(10, 10, 30, 50, 10, 10);
    g.fillRoundRect(55, 65, 55, 30, 5, 5);
  }               
}

Voyez le résultat en figure suivante.

Rendu de la méthode drawRoundRect()

La méthode drawLine()

Cette méthode permet de tracer des lignes droites. Il suffit de lui spécifier les coordonnées de départ et d'arrivée de la ligne. Dans ce code, je trace les diagonales du conteneur :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel { 
  public void paintComponent(Graphics g){
    //x1, y1, x2, y2
    g.drawLine(0, 0, this.getWidth(), this.getHeight());
    g.drawLine(0, this.getHeight(), this.getWidth(), 0);
  }               
}

Le résultat se trouve à la figure suivante.

Rendu de la méthode drawLine()

La méthode drawPolygon()

Grâce à cette méthode, vous pouvez dessiner des polygones de votre composition. Eh oui, c'est à vous de définir les coordonnées de tous les points qui les forment ! Voici à quoi elle ressemble

1
drawPolygon(int[] x, int[] y, int nbrePoints);

Le dernier paramètre est le nombre de points formant le polygone. Ainsi, vous n'aurez pas besoin d'indiquer deux fois le point d'origine pour boucler votre figure : Java la fermera automatiquement en reliant le dernier point de votre tableau au premier. Cette méthode possède également son homologue pour dessiner des polygones remplis : fillPolygon().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){
    int x[] = {20, 30, 50, 60, 60, 50, 30, 20};
    int y[] = {30, 20, 20, 30, 50, 60, 60, 50};
    g.drawPolygon(x, y, 8);

    int x2[] = {50, 60, 80, 90, 90, 80, 60, 50};
    int y2[] = {60, 50, 50, 60, 80, 90, 90, 80};
    g.fillPolygon(x2, y2, 8);
  }               
}

Voyez le résultat à la figure suivante.

Rendu des méthodes drawPolygon() et fillPolygon()

Il existe également une méthode qui prend exactement les mêmes arguments mais qui, elle, trace plusieurs lignes : drawPolyline().

Cette méthode va dessiner les lignes correspondant aux coordonnées définies dans les tableaux, sachant que lorsque son indice s'incrémente, la méthode prend automatiquement les valeurs de l'indice précédent comme point d'origine. Cette méthode ne fait pas le lien entre la première et la dernière valeur de vos tableaux. Vous pouvez essayer le code précédent en remplaçant drawPolygon() par cette méthode.

La méthode drawString()

Voici la méthode permettant d'écrire du texte. Elle est très simple à utiliser : il suffit de lui passer en paramètre la phrase à écrire et de lui spécifier à quelles coordonnées commencer.

1
2
3
4
5
6
7
8
import java.awt.Graphics;
import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){
    g.drawString("Tiens ! Le Site du Zéro !", 10, 20);
  }               
}

Le résultat se trouve à la figure suivante.

Rendu de la méthode drawString()

Vous pouvez aussi modifier la couleur (la modification s'appliquera également pour les autres méthodes) et la police d'écriture. Pour redéfinir la police d'écriture, vous devez créer un objet Font. Le code suivant illustre la façon de procéder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){                
    Font font = new Font("Courier", Font.BOLD, 20);
    g.setFont(font);
    g.setColor(Color.red);          
    g.drawString("Tiens ! Le Site du Zéro !", 10, 20);                
  }               
}

Le résultat correspond à la figure suivante.

Changement de couleur et de police d'écriture

La méthode drawImage()

Voici à quoi elle ressemble :

1
drawImage(Image img, int x, int y, Observer obs);

Vous devez charger votre image grâce à trois objets :

  • un objet Image ;
  • un objet ImageIO ;
  • un objet File.

Vous allez voir que l'utilisation de ces objets est très simple. Il suffit de déclarer un objet de type Image et de l'initialiser en utilisant une méthode statique de l'objet ImageIO qui, elle, prend un objet File en paramètre. Ça peut sembler compliqué, mais vous allez voir que ce n'est pas le cas… Notre image sera stockée à la racine de notre projet, mais ce n'est pas une obligation. Dans ce cas, faites attention au chemin d'accès de votre image.

En ce qui concerne le dernier paramètre de la méthode drawImage, il s'agit de l'objet qui est censé observer l'image. Ici, nous allons utiliser notre objet Panneau, donc this.

Cette méthode dessinera l'image avec ses propres dimensions. Si vous voulez qu'elle occupe l'intégralité de votre conteneur, utilisez le constructeur suivant : drawImage(Image img, int x, int y, int width, int height, Observer obs).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){
    try {
      Image img = ImageIO.read(new File("images.jpg"));
      g.drawImage(img, 0, 0, this);
      //Pour une image de fond
      //g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
    } catch (IOException e) {
      e.printStackTrace();
    }                
  }               
}

Les résultats se trouvent aux deux figures suivantes (pour bien vous montrer la différence, j'ai créé une fenêtre plus grande que l'image).

Conservation de la taille d'origine de l'image

Adaptation de la taille de l'image

L'objet Graphics2D

Ceci est une amélioration de l'objet Graphics, et vous allez vite comprendre pourquoi.

Pour utiliser cet objet, il nous suffit en effet de caster l'objet Graphics en Graphics2D (Graphics2D g2d = (Graphics2D) g), et de ne surtout pas oublier d'importer notre classe qui se trouve dans le package java.awt. L'une des possibilités qu'offre cet objet n'est autre que celle de peindre des objets avec des dégradés de couleurs. Cette opération n'est pas du tout difficile à réaliser : il suffit d'utiliser un objet GradientPaint et une méthode de l'objet Graphics2D.

Nous n'allons pas reprendre tous les cas que nous avons vus jusqu'à présent, mais juste deux ou trois afin que vous voyiez bien la différence. Commençons par notre objet GradientPaint ; voici comment l'initialiser (vous devez mettre à jour vos imports en ajoutant import java.awt.GradientPaint) :

1
GradientPaint gp = new GradientPaint(0, 0, Color.RED, 30, 30, Color.cyan, true);

Alors, que signifie tout cela ? Voici le détail du constructeur utilisé dans ce code :

  • premier paramètre : la coordonnée x où doit commencer la première couleur ;
  • deuxième paramètre : la coordonnée y où doit commencer la première couleur ;
  • troisième paramètre : la première couleur ;
  • quatrième paramètre : la coordonnée x où doit commencer la seconde couleur ;
  • cinquième paramètre : la coordonnée y où doit commencer la seconde couleur ;
  • sixième paramètre : la seconde couleur ;
  • septième paramètre : le booléen indiquant si le dégradé doit se répéter.

Ensuite, pour utiliser ce dégradé dans une forme, il faut mettre à jour notre objet Graphics2D, comme ceci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class Panneau extends JPanel {
  public void paintComponent(Graphics g){
    Graphics2D g2d = (Graphics2D)g;         
    GradientPaint gp = new GradientPaint(0, 0, Color.RED, 30, 30, Color.cyan, true);                
    g2d.setPaint(gp);
    g2d.fillRect(0, 0, this.getWidth(), this.getHeight());                
  }               
}

Les deux figures suivantes représentent les résultats obtenus, l'un avec le booléen à true, et l'autre à false.

Dégradé répété

Dégradé stoppé

Votre dégradé est oblique (rien ne m'échappe, à moi :-p). Ce sont les coordonnées choisies qui influent sur la direction du dégradé. Dans notre exemple, nous partons du point de coordonnées (0, 0) vers le point de coordonnées (30, 30). Pour obtenir un dégradé vertical, il suffit d'indiquer la valeur de la seconde coordonnée x à 0, ce qui correspond à la figure suivante.

Dégradé horizontal

Voici un petit cadeau :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D; 
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class Panneau extends JPanel { 
  public void paintComponent(Graphics g){
    Graphics2D g2d = (Graphics2D)g;
    GradientPaint gp, gp2, gp3, gp4, gp5, gp6; 
    gp = new GradientPaint(0, 0, Color.RED, 20, 0, Color.magenta, true);
    gp2 = new GradientPaint(20, 0, Color.magenta, 40, 0, Color.blue, true);
    gp3 = new GradientPaint(40, 0, Color.blue, 60, 0, Color.green, true);
    gp4 = new GradientPaint(60, 0, Color.green, 80, 0, Color.yellow, true);
    gp5 = new GradientPaint(80, 0, Color.yellow, 100, 0, Color.orange, true);
    gp6 = new GradientPaint(100, 0, Color.orange, 120, 0, Color.red, true);

    g2d.setPaint(gp);
    g2d.fillRect(0, 0, 20, this.getHeight());               
    g2d.setPaint(gp2);
    g2d.fillRect(20, 0, 20, this.getHeight());
    g2d.setPaint(gp3);
    g2d.fillRect(40, 0, 20, this.getHeight());
    g2d.setPaint(gp4);
    g2d.fillRect(60, 0, 20, this.getHeight());
    g2d.setPaint(gp5);
    g2d.fillRect(80, 0, 20, this.getHeight());
    g2d.setPaint(gp6);
    g2d.fillRect(100, 0, 40, this.getHeight());
  }               
}

Maintenant que vous savez utiliser les dégradés avec des rectangles, vous savez les utiliser avec toutes les formes. Je vous laisse essayer cela tranquillement chez vous.


  • Pour créer des fenêtres, Java fournit les composants swing (dans javax.swing) et awt (dans java.awt).
  • Il ne faut pas mélanger les composants swing et awt.
  • Une JFrame est constituée de plusieurs composants.
  • Par défaut, une fenêtre a une taille minimale et n'est pas visible.
  • Un composant doit être bien paramétré pour qu'il fonctionne à votre convenance.
  • L'objet JPanel se trouve dans le package javax.swing.
  • Un JPanel peut contenir des composants ou d'autres conteneurs.
  • Lorsque vous ajoutez un JPanel principal à votre fenêtre, n'oubliez pas d'indiquer à votre fenêtre qu'il constituera son content pane.
  • Pour redéfinir la façon dont l'objet est dessiné sur votre fenêtre, vous devez utiliser la méthode paintComponent() en créant une classe héritée.
  • Cette méthode prend en paramètre un objet Graphics.
  • Cet objet doit vous être fourni par le système.
  • C'est lui que vous allez utiliser pour dessiner dans votre conteneur.
  • Pour des dessins plus évolués, vous devez utiliser l'objet Graphics2D qui s'obtient en effectuant un cast sur l'objet Graphics.