BoutonCarte

Créer une classe est utile pour personnaliser le graphisme d’un JButton (ou JLabel, JPanel …) et ajouter des fonctionnalités. Ci-dessous nous allons transformer un JButton en carte de jeu (cliquable).

Une classe héritant de JButton

Créer une interface graphique

Créer un Java Project. Puis créer une classe (le programme principal) avec une interface graphique GUI avec  extends JFrame.

Utiliser WindowBuilder pour y placer un JButton (c’est cet objet qui sera re-stylisé par la suite).

Créer une autre classe (à côté de la GUI)

Le but de cette partie est de créer une autre Classe dans le même Java Project que la GUI, comme ceci :

      

Une classe est un fichier .java  dans un répertoire /src dans le répertoire du projet.
Un nom de classe commence par une Majuscule.

  • Clic sur l’icone ja_NewClassIcone (dans la barre d’icônes) – OU – clic droit ( New > Class )
  • Donner un nom de classe COMMENÇANT PAR UNE MAJUSCULE, exemple :  BoutonCarte
  • Cocher la création du constructeur (ne pas créer la méthode main() ). Puis Finish.

faire hériter cette classe de JButton

Bien entendu, on ne va pas construire entièrement cette nouvelle classe !!! Alors on va réutiliser la classe déjà existante JButton et rajouter des particularités.

Pour que la nouvelle classe hérite de la classe JButton, on ajoute extends JButton

  • Ajouter  extends JButton  sur la nouvelle classe.
  • Faire Ctrl+Shift+O pour supprimer l’erreur ja_ampoulError et importer la librairie de JButton.
  • Mettre dans le constructeur l’instruction super(); (cette 1ère instruction construit d’abord un JButton classique … qui sera stylisé par la suite)
  • Facultatif : On peut supprimer l’avertissement souligné en jaune : clic sur l’ampoule jaune ja_ampoulAvert > Add default serial ID. Une variable globale (champ de classe) serialVersionUID se crée.

ajouter un champ ( + tableau)

On voudrait que ce BoutonCarte représente un as ou roi, dame, … Donc on va créer une variable globale figure (appelée champ de classe) et un tableau de toutes les cartes possibles tabFigure.

Pourquoi créer un tableau des possibles ?

Car plus tard, on aura besoin de choisir une carte au hasard, alors le plus simple sera de choisir aléatoirement un élément du tableau.

  • La variable figure est de type String  et on peut l’initialiser à As, d’où l’instruction    String figure = "As";
  • Le tableau tabFigure est l’ensemble des cartes possibles (ce tableau sera utile pour choisir aléatoirement une carte).
String figure = "As";
String[] tabFigure = new String[] {"As", "2", "3", "4", "5", "6", "7", "8", "9", "10", "V", "D", "R"};

Ajouter un ‘getter’ et un ‘setter’ (facultatif)

Pour pouvoir modifier et récupérer la valeur de la carte depuis une autre classe (la GUI), on peut utiliser 2 méthodes (un ‘getter’ et un ‘setter’) . Une autre solution est d’accéder directement à la variable en mettant un point.

Ja_CreateGetterSetter

  • Sélectionner le nom de la variable (donc figure).
  • Taper Ctrl+1 ou menu : Edit > Quick Fix
  • Clic sur « Create getter and setter ».
  • S’ouvre une fenêtre pour modifier (éventuellement) les noms des méthodes (getter et setter). Valider.

Ja_CreateGetterSetter2
Puis programmer ces méthodes :

  • Le ‘getter’ consiste simplement à renvoyer la valeur de la variable figure d’où l’instruction : return figure;
  • Le ‘setter’ consiste à remplacer la valeur de la variable figure par la valeur passée en paramètre nlleFigure  et ré-actualiser l’affichage.
    Si on était perfectionniste, on vérifierait d’abord que cette nouvelle figure nlleFigure est bien un élément du tableau tabFigure avant d’accepter de faire la modification.
public class BoutonCarte extends JButton {
	/**
	 * Classe : BoutonCarte : un JButton en forme de "carte de jeu"
	 *  => figure est la valeur de la carte "As" ou "R" ou "D" ...
	 * figure est un élément du tableau 'tabigure'={}
	 */
	private static final long serialVersionUID = 1L;
	String[] tabFigure = {"As", "2", "3", "4", "5", "6", "7", "8", "9", "10", "V", "D", "R"}; 
	String figure="As";

	public BoutonCarte() { // constructeur
		super(); //on construit d'abord un JButton normal (ça permet setText ...)
		setText(figure);
	}

	public String getFigure() { // Le 'getter'
		return figure;
	}

	public void setFigure(String nllefigure) { // Le 'setter'
		figure = nllefigure;
		setText(figure);
	}
}

Ajouter d’autres méthodes

Ajoutons, par exemple, une autre méthode changeFigure() qui choisit aléatoirement une carte dans le tableau des possibles tabFigure.

Ce qui faut savoir sur un tableau (sur un exemple raccourci) :  tabFigure = { "As", "V", "D", "R" }

  • Chaque élément possède un index, et le 1er élément porte l’index 0. Donc : le premier élément est  tabFigure[0] = "As", puis  tabFigure[1] = "V", tabFigure[2] = "D" et le dernier :  tabFigure[3] = "R".
  • La longueur du tableau tabFigure s’obtient avec tabFigure.length  donc ici tabFigure.length = 4  (il faut toujours utiliser tabFigure.length au lieu de 4 dans le code, car le tableau tabFigure peut s’agrandir ou rétrécir au cours du projet).
  • Finalement, le tableau tabFigure est indexé de 0  jusqu’à l’index tabFigure.length-1

Finalement choisir aléatoirement un élément du tableau consiste à choisir un index (un entier aléatoire compris entre  0  et tabFigure.length-1 ) :

Random random = new Random(); //construction de valeurs aléatoires
int i = random.nextInt(tabFigure.length);//entier aléatoire entre 0 et tabFigure.length-1 
figure = tabFigure[i];

Bien sûr, ce tirage au sort peut redonner la même figure qui était déjà sur la carte !! … Ce qui est un peu embêtant … alors on peut exiger que la nouvelle carte choisie au hasard (donc tabFigure[i]) soit différente de l’ancienne carte (la variable de champ figure) : il suffit de recommencer le tirage au sort de i tant que ces 2 cartes sont égales.
Attention : pas de == pour l’égalité de String  mais equals() comme ceci :   figure.equals(tabFigure[i]).

Finalement (dans la classe BoutonCarte) :

  1. Placer dans les variables générales :  Random random  puis Ctrl+Shift+O pour importer la librairie Random. Ainsi on déclare une liste aléatoire random.
  2. Ajouter dans le constructeur BoutonCarte() :  random = new Random();  pour construire la liste de valeurs aléatoires
  3. Ajouter une méthode changeFigure() :
    public void changeFigure() {	
    //recalcule une figure aléatoire différente de la figure actuelle  
        int i;
        do {
            i = random.nextInt(tabFigure.length);
        } while (figure.equals(tabFigure[i]));
    
    //puis on change la valeur du champ "figure" et l'affiche sur le btn
        figure = tabFigure[i];
        setText(figure);
    }

Résumé

Ecrire une classe consiste à écrire une librairie de méthodes et/ou de constructeurs (de la même façon que l’auteur de la classe JButton a écrit les méthodes setText() , getText() … et des constructeurs de JButton).

Cette classe BoutonCarte est maintenant terminée : on a écrit un constructeur et les méthodes getFigure(), setFigure(), changeFigure().

Prochaine étape : utiliser un objet de cette classe BoutonCarte dans le programme principal (la GUI).

import java.util.Random;
import javax.swing.JButton;

public class BoutonCarte extends JButton {
  private static final long serialVersionUID = 1L;
  
  String figure = "As";
  String[] tabFigure = new String[] {"As", "V", "D", "R"};
  Random random;

  public BoutonCarte() {
    super();
    setText(figure);
    random=new Random();
  }

  public String getFigure() { //le 'getter'
    return figure;
  }

  public void setFigure(String nllefigure) { //le 'setter'
    figure = nllefigure;
    setText(figure);
  }
  
  public void changeFigure() { //met une figure aléatoire
    int i;
    do {
        i = random.nextInt(tabFigure.length);
    } while (figure.equals(tabFigure[i]));
    figure = tabFigure[i];
    setText(figure);
  }
}

Insertion dans l’autre classe (la GUI)

De retour dans le programme principal (la GUI), on a déjà placé un JButton avec WindowBuilder (en variable globale ja_eclipVarGlobal).

Donc on va modifier le type JButton en la classe BoutonCarte.
    ja_boutonCarteLettre

Ajouter un Listener

Pour que cette classe soit un bouton cliquable, il faut ajouter un Listener.

Il y a 2 possibilités :

  • soit l’ajouter dans le programme principal (la GUI).
  • soit ajouter le Listener dans la classe (déconseillé car la GUI n’est plus accessible en la fin de partie).

Listener dans la GUI (conseillé)

Pour ajouter un ActionListener sur un bouton (de cette classe),  il suffit d’ouvrir la GUI avec WindowBuilder et de double cliquer sur le bouton pour qu’un ActionListener se créé. Si besoin, faire Ctrl+Shift+O pour importer la librairie ActionListener.

monBouton = new BoutonCarte();
monBouton.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    monBouton.changeFigure();
  }
});
monBouton.setBounds(200, 200, 50, 50);
getContentPane().add(monBouton);

 

Tableau de boutons & Listener dans GUI

Si on utilise un tableau de BoutonCarte alors pour retrouver le bouton qui a cliqué, c’est plus compliqué :

  • il faut rechercher la source de l’événement en appliquant la méthode getSource() sur l’ActionEvent (exemple ci-dessous cet ActionEvent est arg0) : arg0.getSource()
  • puis il faut transtyper cette source en un objet de la classe BoutonCarte (pour pouvoir utiliser les méthodes de cette classe, comme changeFigure() ) : (BoutonCarte) arg0.getSource()
    for (int i = 0; i < tabBtn.length; i++) {
      for (int j = 0; j < tabBtn[i].length; j++) {
        tabBtn[i][j] = new BoutonCarte();
        tabBtn[i][j].addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent arg0) {
            ((BoutonCarte) arg0.getSource()).changeFigure();
          }
        });
        tabBtn[i][j].setBounds(30+50*i, 30+50*j, 50, 50);
        getContentPane().add(tabBtn[i][j]);
      }
    }

=============================

Listener dans la classe BoutonCarte

  1. Ajouter implements ActionListener  sur la class, juste à côté de extends JButton (on dit qu’ainsi la classe implémente l’interface ActionListener).
  2. Taper sur Ctrl+Shift+O pour importer la librairie ActionListener.
  3. Puis cliquer sur l’ampoule-erreur   (ou sur le nom de la classe BoutonCarte souligné en rouge ou QuickFix), puis Add unimplemented methods pour que la méthode actionPerformed s’intègre dans la classe.
  4. Puis remplir la méthode actionPerformed(). [ NB : On peut enlever l’avertissement @Override et le //TODO ]
  5. Enfin, il faut ajouter cet ActionListener au bouton,
    • soit dans la classe BoutonCarte, dans le constructeur : addActionListener(this);
    • soit dans le programme principal GUI, dans le constructeur :
      monBouton = new BoutonCarte();
      monBouton.setBounds(30, 30, 50, 50);
      monBouton.addActionListener(monBouton);
      getContentPane().add(monBouton);

Tableau de boutons & Listener dans classe BoutonCarte

Pour construire un tableau de boutons (avec Listener dans la classe BoutonCarte) et addActionListener() dans le programme principal GUI, on procède ainsi :

  • dans la GUI : déclarer le tableau de BoutonCarte dans les variables générales :  BoutonCarte tabBtn [][] = new BoutonCarte [3][3];
  • dans le constructeur de la GUI, construire le tableau de BoutonCarte :
    for (int i = 0; i < tabBtn.length; i++) {
      for (int j = 0; j < tabBtn[i].length; j++) {
        tabBtn[i][j] = new BoutonCarte();
        tabBtn[i][j].setBounds(30+50*i, 30+50*j, 50, 50);
        tabBtn[i][j].addActionListener(tabBtn[i][j]);
        getContentPane().add(tabBtn[i][j]);
      }
    }

=========================================================

Graphisme

Ja_GraphBtnImageImgimage sur un JButton

Avec package : Cet exemple ayant pour package fr.isn  :

JButton btnTelecharger = new JButton("Télécharger");
btnTelecharger.setIcon(new ImageIcon("bin/fr/isn/imgDownload.png"));

Ja_GraphBtnImageEnsuite il faut placer l’image à 2 endroits :

  1.  dans le répertoire  /src  >  package > à côté des fichiers .java
  2.  dans le répertoire  /bin  >  package > à côté des fichiers .class

Mais cette solution est temporaire … par la suite il faudra créer une classe ResourceLoader pour importer correctement les ressources.

Sans package : L’exemple précédent, sans package, devient :

JButton btnTelecharger = new JButton("Télécharger");
btnTelecharger.setIcon(new ImageIcon("bin/imgDownload.png"));

Et placer l’image dans les 2 répertoires /src et /bin (donc à côté des fichiers .java et .class).

Idem, c’est une solution temporaire … car une classe ResourceLoader est conseillée, voire nécessaire pour faire fonctionner correctement un exécutable JAR.

NB : il existe aussi des méthodes pour rajouter des effets au JButton :

  • bouton.setPressedIcon(Icon i);
  • bouton.setRolloverIcon(Icon i);
  • bouton.setRolloverSelectedIcon(Icon i);
  • bouton.setDisabledIcon(Icon i);

dessiner

Il s’agit de réécrire la méthode qui dessine le fond d’un bouton (on « surcharge la méthode paintComponent() »).

public class Bouton extends JButton {
	private String name; 
	public Bouton(String str) { // constructeur
		super(str); //construit d'abord un JButton contenant un texte
		this.name = str;
		this.setContentAreaFilled(false);
//mettre un fond transparent pour pouvoir redessiner le fond
	}
	public void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g; // transtypage de Graphics -> Graphics2D
		GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
		g2d.setPaint(gp);
		g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
		super.paintComponent(g2d); // dessine l'étiquette encadrée par dessus le fond
	}
}

ja_boutonDegrade

ResourceLoader pour les images et sons

Pour charger correctement des fichiers Ressource, il y a beaucoup de variantes possibles qui dépendent de l’arborescence du projet (présence d’un package ou pas) et du lieu de stockage des images … et il faut différencier :

  • chemin absolu :  “/path/image.jpg” : chemin utilisé tel quel.
  • chemin relatif : “path/image.jpg” : ce chemin démarre de la méthode qui l’appelle.

Résumé de la méthode (détaillée ci-dessous) :

  1. Créer un Source Folder « res » à la racine du Java Project (… donc à l’extérieur du package).
  2. Créer une classe ResourceLoader dans ce répertoire « res » et nommer le package « main » .
  3. Créer un folder « images » dans ce package « main » et y mettre les images.
  4. Dans la GUI (ou classe java important des images), ajouter la librairie import main.ResourceLoader;  puis dans le code, on peut l’utiliser ainsi :
    BufferedImage im = ResourceLoader.loadImage("images/toto.jpg");

La méthode en détail … et en images :

  • Sélectionner le Java Project et y créer un Source Folder « res » (clic droit : New > Source Folder).
    ja_resourceFolder     ja_SourceFolderName
  • Sélectionner le dossier « res" et y créer un package « main » (clic droit sur le Source Folder res : New > Package).
    ja_SourcePackage      ja_SourcePackageName
  • Sélectionner le package « main » et créer une classe ja_NewClassIcone  ResourceLoader   (clic droit sur le package main : New > Class).
    ja_classResourceLoader
  • Copier ce code qui permet d’obtenir des objets de type BufferedImage :
    package main;
    
    import java.awt.Image;
    import java.awt.Toolkit;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.URL;
    import javax.imageio.ImageIO;
    public class ResourceLoader {
       static ResourceLoader rl = new ResourceLoader();
       public static Image getImage(String fileName){   
            /**  Usage => chemin relatif (par rapport à ResourceLoader) :
             *  * Si l'image est à la racine (à côté de ResourceLoader.java) : 
             *          fileName = "toto.jpg"
             *  
             *  * Si  l'image est dans un dossier /images :
             *         fileName = "/images/toto.jpg" 
             *      ou URL url = rl.getClass().getResource("images/" + fileName);
             */
           URL url = rl.getClass().getResource(fileName);
           return Toolkit.getDefaultToolkit().getImage(url);
       }
        
       public static BufferedImage loadImage(String pathRelativeToThis) throws IOException {
            URL url = rl.getClass().getResource(pathRelativeToThis);
            BufferedImage img = ImageIO.read(url);
            return img;
        }
    }
  • A l’intérieur du package main, créer un Folder images .
    ja_mainNewFolder   ja_resourceImage2
  • Faire glisser vos images dans ce dossier images.
    ja_resourceImages
  • Dans le code java, il faut :
    • ajouter cette librairie :  import main.ResourceLoader;
    • ajouter dans les variables générales :  BufferedImage imageSprites;  [ et faire Ctrl+Shift+O pour importer la librairie BufferedImage]
    • ajouter dans le constructeur :  imageSprites = ResourceLoader.loadImage("images/jeu54cartes.png");
    • puis entourer cette dernière instruction avec try/catch.

0_panneau_attention Bien respecter les  majuscules/minuscules  et les  extensions  :  jpg  ou  jpeg  ou  JPG …

0_panneau_attention Si vous modifiez une image (ou toute autre ressource) alors il faut absolument nettoyer le projet (sinon Java ne renouvelle pas ses anciennes ressources compilées) => dans le menu : Project > Clean > cocher le JavaProject.