Welcome to Bukkit France

Inscrivez-vous maintenant pour profiter d'un accès total à tout le contenu offert par la meilleur communauté Bukkit française ! Une fois inscrit et connecté, vous pourrez contribuez à la communauté en postant vos propres sujets et questions ou en répondant à ceux existants. Vous pourrez aussi customiser votre profil, recevoir des points de réputations, communiquer avec les autres membres via le chat, et plus encore! 

  • Annonces

    • Pskyco

      Bukkit France passe sous Discord !   02/20/16

      Bukkit France est désormais passé sur Discord, au revoir donc notre vieux Teamspeak ! Téléchargez le client et venez nous rejoindre sur notre salon en suivant les instructions suivantes.
      M-à-j du 25/02/2017 : Désormais, seuls les comptes actifs sur le forum se verront donner l'accès au Discord, ce dernier n'est pas une plateforme d'aide de la même manière que le chat.
  • billets
    47
  • commentaires
    40
  • vues
    32234

À propos de ce blog

Cours de Java

Billets dans ce blog

SystemGlitch

Prologue

blog-0426494001431880602.png

Bonjour chers/chères Bukkitiens/Bukkitiennes !

 

Bienvenue dans le cours de développement Java! Que pensez-vous de commencer par l'introduction? :lol:

 

 

 

Java32324.png

 

 

Java, non, on ne parle pas de la danse ridicule des années 1920!

 

Le Java est un langage de programmation apparu en 1995, et oui! 1995! :o Mais il était alors bien différent de celui que nous connaissons aujourd'hui! Il fut développé par l'entreprise Sun MicroSystems qui fut rachetée par Oracle Corporation en 2009. Il n'y pas si longtemps que ça finalement!

 

Java est un langage orienté objet robuste, puissant et surtout, très portable car il est multi-plateforme.

Sa grande portabilité réside dans un concept utilisé auparavant par le Pascal, il utilise une machine virtuelle que l'on nomme Java Runtime Environment (JRE), qui va faire l'intermédiaire entre le bytecode (écrit par le compilateur grâce au code de l'utilisateur), et va le transformer en langage compréhensible par la machine concernée. Il y a un autre aspect de sa "portabilité", se fût à sa création, sa syntaxe ressemble beaucoup à celle du C++ et ce n'est pas une coïncidence, les ingénieurs de l'époque travaillaient essentiellement en C, reprendre cette syntaxe permettait à une large communauté de pouvoir utiliser leur langage très rapidement, et c'est en grande partie grâce à cela que le Java est devenu vite populaire.

Sa puissante API permet de gérer un grand nombre de domaines comme les interfaces graphiques, la gestion des fichiers, que ce soit en local ou sur une base de données, et bien plus encore!

 

Ses possibilités sont très diversifiées. Les bases sont faciles à intégrer mais maîtriser les différents domaines demande plusieurs années de pratique... Mais ne vous inquiétez pas, nous irons pas-à-pas.

 

Contrairement à d'autres cours sur Internet, nous ne nous contenterons pas de dresser une liste exhaustive des possibilités du Java et de Bukkit car ce serait trop long et inutile! Nous ne somme pas là pour faire du bourrage de crane! Nous voulons vous fournir les outils nécessaires pour que vous sachiez vous débrouiller tout seul, sans être un assisté qui se contente de recopier un tutoriel!

Et plus important encore, vous comprendrez cette superbe vidéo!

 

 

 

 

 

 

 

Ne plongez pas la tête la première!

 

Afin de développer avec ce merveilleux langage, il vous faut d'abord installer Java, mais pas une simple JRE, la version Java SE Development Kit (JDK).

Nous tenons aussi à signaler que rien ne vous empêche d'installer plusieurs JDK et de choisir celle que vous voulez pour votre projet, via votre IDE.

 

Il est évidemment possible de développer à l'aide d'un simple éditeur de texte, mais avouez que l'idée même en est repoussante! Pour développer, il vous faudra des couleurs, de l'ordre et un environnement agréable, afin de mieux s'y retrouver au sein de son code. On appelle ces logiciels IDE (Integrated development environment). Nous allons explorer le monde du Java avec un IDE répondant au nom d'Eclipse. Vous pouvez télécharger la version dont nous aurons besoin ici!

 

Vous voila prêt à commencer me direz-vous! Et bien désolé de vous décevoir, mais non, il y a encore des choses à savoir avant de débuter. Vous vous doutez que ce genre d'outil n'est pas ce qu'il y a de plus simple à utiliser. :mellow:

 

Je pense que vous n'aurez pas trop de difficultés à apprendre les différents raccourcis clavier, ceux-ci sont spécifiés dans les menus.

Ceux-ci sont les plus importants:

  • Ctrl + Shift + O : Il vous permet de rapidement générer les imports dont vous aurez besoin pour faire fonctionner votre code.
  • Ctrl + S : Il permet de sauvegarder la classe sur laquelle vous travaillez actuellement.
  • Ctrl + Shift + S : Il permet de tout sauvegarder. Toutes les classes modifiées depuis la dernière sauvegarde seront sauvegardées !

 

Jetez un œil à cette rapide visite guidée de votre IDE :

 

Se8dlHS.png

 

 

Vous pourrez trouver plus d'informations à propos de votre IDE en consultant la documentation officielle d'Eclipse. Nous apprendrons tout de même quelques unes de ses fonctionnalités ensemble!

 

Lancez Eclipse. Une boite de dialogue apparaîtra. Spécifiez le dossier dans lequel vous voulez que tous vos projets soient sauvegardés. On appelle ce dossier "Workspace".

 

Faites un clic droit dans l'explorateur de packages et créez un nouveau projet :

 

LGske6A.png

 

Une boite de dialogue s'ouvrira pour vous demander de définir les paramètres de votre projet. Assurez-vous que ces derniers soient bien les mêmes que sur l'image suivante :

 

Akv3Wo5.png

 

Cliquez sur "Finish". Votre projet est créé! Nous le remplirons plus tard! Maintenant, passons aux choses sérieuses!

 

 

JeremGamer et JackBlue

 

tNIpRtq.png

SystemGlitch

blog-0469592001437294857.png

Vous possédez un Raspberry Pi et vous souhaitez en faire un peu plus qu'un simple media-center? Il est tant de s'amuser un peu! Nous allons programmer notre Raspberry Pi en Java et le faire interagir avec le monde réel à l'aide de capteurs et de dipôles.

Notre projet sera le suivant: nous souhaitons allumer une LED lorsqu'un capteur détecte un mouvement, en passant par le logiciel afin de pouvoir étendre les perspectives d'évolution du prototype. :)

 

Cette vidéo que j'ai réalisée illustre le résultat final :

 

 

Je tiens à préciser que je considèrerai que vous disposez de connaissances basiques en électricité ainsi que de tout ce que je vous ai appris sur le Java jusque là. ;)

 

Matériel :

 

  • Un Raspberry Pi, de n'importe quel modèle. Ici j'utilise le modèle 2 B, le plus récent.
  • Tout ce qu'il vous faut pour le faire fonctionner pour une utilisation basique, à savoir:
    • Un câble d'alimentation 5V 2A
    • Un écran
    • Un clavier
    • Une souris
    • Un dongle Wifi (ou un câble Ethernet, nous aurons besoin d'une connexion internet)
    • Une carte Micro-SD avec Raspbian installé dessus
  • 3 câbles de montage femelle/femelle

  • 2 câbles de montage mâle/femelle

  • 1 câble de montage mâle/mâle

  • Une résistance 270Ω

  • Une LED

  • Un capteur PIR (de mouvement)

  • Une breadboard pour tout assembler

 

 

Vous pourrez trouver tout ce matériel sur le site ModMyPi pour des prix raisonnables et une livraison rapide. ;) Même si vous ne faites pas le montage de vos mains, rien ne vous empêche de quand même suivre ce chapitre. :lol:

 

Pour commencer, nous allons installer Java sur la machine. Ouvrez un terminal depuis votre Raspberry Pi (en considérant que vous le faites tourner sous Raspbian) et tapez la commande suivante, en vous assurant au préalable de bien être connecté à internet :

sudo apt-get update && sudo apt-get install oracle-java7-jdk

 

En attendant que tout cela s'installe, lancez Eclipse, nous allons préparer notre projet.

Nous aurons besoin de l'API Pi4J. Créer un nouveau projet et ajoutez l'API fraichement téléchargée au Build Path. Tout est prêt, passons désormais à la partie qui vous est la plus étrangère: le montage.

 

Tout d'abord, il faut savoir quelques petites choses. Le Raspberry Pi 2 B est doté de 40 broches dont 26 GPIOs (General Purpose Input/Output). Nous utiliserons ces broches pour connecter nos capteurs et dipôles. Nous ne pourrons pas utiliser certaines de ces broches, ces dernières sont prévues pour d'autres utilisations. Cependant attention, il faut brancher les choses là où il faut! Certaines broches ne sont pas des GPIOs comme je l'ai dis juste au dessus. Certaines délivreront simplement une tension (3.3V ou 5V), d'autres ne délivreront rien (on les appelle Ground, pour fermer le circuit électrique). Chaque GPIO peut être utilisé soit en entrée, soit en sortie, comme son nom l'indique. Le principe est simple, comme dans un ordinateur classique, le Raspberry interprètera la tension qui se trouve dans les câbles quand il s'agit d'une entrée, et la fera changer lorsqu'il s'agit d'une sortie. Tout cela sera géré par votre code! N'est-ce pas fabuleux? ^_^

Comment savoir quelle broche porte quel numéro? Encore une fois, la documentation est là pour nous sauver la vie !

ZxTsV62.png

Nous voila mieux renseignés ! Cependant il reste un problème en suspens... Comment savoir de quel côté commencer à compter? Cette fois c'est le site du constructeur qui nous sauve ! Il faut avoir les bons réflexes quand on ne sait pas quelque chose! ;) Mais vu que je le sais, je vous le dis, ce serait égoïste sinon. :lol:

La broche n°1 (délivrant du 3.3V, le carré orange sur l'image ci-dessus) est la broche la plus proche du port pour carte Micro-SD sur le Raspberry. C'est à dire la broche à l'extrême opposé des ports USB et vers l'intérieur de la carte. Vous voyez de laquelle il s'agit? Très bien, passons à la suite ! :P

 

Le montage de la LED sera effectué sur la breadboard. J'imagine que vous ne savez pas de quoi il s'agit, et encore moins comment ça fonctionne ! C'est normal, pas de panique, c'est vraiment très simple. ;)

 

Voici une illustration qui devrait beaucoup vous aider à comprendre le principe. (Source: sparkfun.com)

a3ahdje.jpg

A gauche, la breadboard telle que vous la voyez, et à droite, on lui a retiré son cache en plastique. Vous pouvez donc facilement voir les zones conductrices et celles qui sont reliées entre elles ou non. Les flèches rouges pointes les rails d'alimentation. Vous brancherez les câbles directement reliés à l'alimentation (les broches 1, 2, 4 et 17) sur le rail + et le câble fermant le circuit sur le rail -, pour ensuite relier ce rail vers une broche Ground sur le Raspberry. Au centre, on a donc toutes les lignes, pas besoin de vous expliquer comment circule le courant électrique! Cependant je tiens quand même à préciser que les branchements devront donc s'effectuer d'une certain manière afin de ne pas générer de court-circuit! Cette image l'illustre très bien: (Source: pbworks.com)

NqU2Ldo.gif

Dernière chose: ces planches sont extrêmement pratiques car elles nous permettent de faire des montages sans avoir de faire quelque soudure que ce soit! De plus, on peut facilement revenir en arrière si on s'est trompé, ou si on veut rajouter quelque chose. :D

 

Voila vous savez désormais vous servir d'une breadboard ! ;)

Intéressons-nous maintenant à notre montage! Voici à quoi il devrait ressembler :

2k1O67e.jpg

cWA1exT.jpg

BqVSDY5.jpg

 

Nous connectons le capteur (à droite) directement sur les broches sans passer par la breadboard; cela ne servirait à rien à part utiliser des câbles supplémentaires. De plus, vu la taille du montage, nous ne sommes pas restreints en broches. ^_^ J'ai soigneusement choisi la couleur des câbles afin de bien s'y retrouver.

En noir, il s'agit des câbles reliés aux broches Ground; en rouge, il s'agit des câbles reliés à l'alimentation; le câble jaune est le câble à connecter sur un GPIO afin que le capteur puisse communiquer avec le Raspberry; et le câble bleu est la fermeture du circuit sur le rail - de la breadboard.

 

Histoire de rende ça plus lisible pour vous, voici une version schématisée du montage : ;)

 

BsD9J8h.png

 

La broche 2 fournira une tension de 5V au capteur PIR, on refermera ce premier petit circuit sur la broche 6 Ground. Le câble jaune pour communiquer avec le Raspberry sera branché sur le GPIO 22, qui se trouve être la broche 31! Attention à cela! Pour bien effectuer le branchement de votre capteur, voici une photo qui devrait vous aider. Il serait dommage de griller votre capteur tout neuf... -_-

 

vJG06xC.jpg

 

Occupons nous désormais du second circuit: celui de la LED. Branchons un câble femelle/mâle sur le GPIO 26, sur la broche 32, juste en face du câble jaune. De l'autre côté, on le branchera sur la breadboard dans la colonne 'a'. Sur la même ligne, branchez la résistance. Cette dernière protège la LED, qui sera régulièrement allumée puis éteinte. Cela risquerait de la griller assez rapidement si l'on ne mettait pas de résistance. Le deuxième pôle de la résistance ira se brancher dans la même colonne mais quatre lignes plus loin. Place à LED désormais. La patte la plus longue (pôle +) ira se brancher sur la même ligne que le pôle - de la résistance. Quand à l'autre, on le branchera sur la ligne juste à côté comme montré sur le schéma. Finissons avec un câble mâle/mâle qui reliera cette dernière ligne au rail -. Plus qu'à ajouter le câble qui refermera le circuit sur le Raspberry sur la broche 6 Ground. Le montage est prêt ! :D

 

Le code :

 

Vous attendiez surement ce moment. Cela vous paraitra d'une simplicité déconcertante vous verrez. ;)

Pour commencer, il faut initialiser et paramétrer tout ça. Créons trois variables d'instance que nous initialiserons dans une méthode :

 
private GpioController gpio;
private GpioPinDigitalInput sensor;
private GpioPinDigitalOutput led;

private void initGPIOs() {
	System.out.println("Initializing GPIOs...");
	gpio = GpioFactory.getInstance();
	led = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_26); //La led est branchée en sortie sur le GPIO 26 (câble rouge de gauche)		

	sensor = gpio.provisionDigitalInputPin(RaspiPin.GPIO_22 , PinPullResistance.PULL_DOWN); //Le capteur est branché en entrée sur le GPIO 22 (câble jaune)
}

 

Voila qui est fait ! ^_^ Mais il manque quelque chose. Comment récupérer les informations venant du capteur? :lol:

Figurez-vous que c'est bien fait! On a tout à notre disposition dans cette formidable API ! :P Nous allons utiliser un event.

sensor.addListener(new GpioPinListenerDigital() { //On utilise un event plutôt qu'une boucle while, c'est bien plus optimisé
 
	@Override
	public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent e) {
		//...
	}
});

 

Nous sommes presque prêts. Créons une petite méthode supplémentaire afin de gérer cela. Il faut savoir que le capteur peut également envoyer un signal pour dire qu'il ne voit pas de mouvement ! Ce qui nous permettra d'éteindre la LED. En réalité, le capteur scanne périodiquement son champ de vision et augmente la tension dans le câble lorsqu'il voit quelque chose. L'event sera alors déclenché assez régulièrement.

private void handleSensorInput(final GpioPinDigitalOutput led, final GpioPinDigitalStateChangeEvent event) {
	if(event.getState().isHigh()) { //Si la tension dans le câble jaune est élevée -> Mouvement détecté par le capteur
		System.out.println("Motion detected");
		led.high(); //On allume la LED en augmentant la tension dans le câble
	} else { //Si la tension dans le câble jaune est faible -> Pas de mouvement
		led.low(); //On éteint la LED en retirant la tension dans le câble
	}
}

 

Je pense qu'il n'y a pas besoin de vous expliquer d'avantage ce code d'un simplicité enfantine. ;)

 

Dernière chose, il faut penser à tout bien faire correctement. Alors pensez à appeler cette méthode lorsque votre logiciel se termine afin de libérer les GPIOs. ^_^

public void shutdown() {
	led.low(); //Au cas où le logiciel soit terminé alors qu'un mouvement est détecté, la LED resterait allumée à l'infini sinon
	gpio.shutdown();
}

 

Le code complet nous donnera quelque chose qui ressemble à cela :

 
private GpioController gpio;
private GpioPinDigitalInput sensor;
private GpioPinDigitalOutput led;
	
private void initGPIOs() {
	System.out.println("Initializing GPIOs...");
	gpio = GpioFactory.getInstance();
	led = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_26); //La led est branchée en sortie sur le pin 26 (câble rouge de gauche sur la vidéo)		
 
	sensor = gpio.provisionDigitalInputPin(RaspiPin.GPIO_22 , PinPullResistance.PULL_DOWN); //Le capteur est branché en entrée sur le pin 22 (câble jaune sur la vidéo)
	sensor.addListener(new GpioPinListenerDigital() { //On utilise un event plutôt qu'une boucle while, c'est bien plus optimisé

		@Override
		public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent e) {
			handleSensorInput(led, e);
		}

	});
}
 
private void handleSensorInput(final GpioPinDigitalOutput led, final GpioPinDigitalStateChangeEvent event) {
	if(event.getState().isHigh()) { //Si la tension dans le câble jaune est élevée -> Mouvement détecté par le capteur
		System.out.println("Motion detected");
		led.high(); //On allume la LED en augmentant la tension dans le câble
	} else { //Si la tension dans le câble jaune est faible -> Pas de mouvement
		led.low(); //On éteint la LED en retirant la tension dans le câble
	}
}
	
public void shutdown() {
	led.low(); //Au cas où le logiciel soit terminé alors qu'un mouvement est détecté, la LED resterait allumée à l'infini sinon
	gpio.shutdown();
}

 

Exportez votre projet dans un Runnable Jar file et stockez-le dans votre Raspberry.

Ouvrez un terminal depuis ce dernier et tapez la commande suivante, en adaptant le nom du Jar ainsi que son chemin :

java -jar /home/pi/Desktop/BukkitPi.jar

 

Cela lancera votre programme! Vous pourrez commencer à tester !

Hum le plaisir aura été de courte durée... <_< Le capteur ne semble pas très bien fonctionner... -_-

Il n'est pas calibré. Vous avez vraiment de la chance de m'avoir ! :lol: Moi qui ai passé bien une heure à la calibrer...

 

Bref, vous avez sans doute remarqué deux petites bornes oranges sur votre capteur. A l'aide d'un petit tournevis, vous pouvez les tourner afin de calibrer le capteur par rapport à vos besoins. L'un modifie la sensibilité, l'autre le délai entre chaque balayage. Terminez votre programme avant de modifier cela, il semblerait que cela fonctionne mieux, pour une raison que je ne saurais expliquer. :lol: Voici mes réglages (faites bien attentions aux repères, il serait dommage d'inverser les deux bornes ^_^ ) :

 

OZpzeoA.jpg

 

Et voila ! Vous pouvez relancer votre programme ! Vous avez réalisé votre premier capteur de mouvement intelligent avec votre Raspberry Pi ! B)

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

ilicos

blog-0436006001446747183.jpg

Bonjour tout le monde,

 

Jackblue semblant être bien trop occupé pour avancer les cours de spigot actuellement, je me suis décidé à m'y mettre et contribuer à l'avancement du programme! :)

Étant programmeur étudiant dans le domaine du jeux-vidéo, je risque de généraliser un peu des concepts avant d'aborder le développement craftbukkit/spigot.

 

I - Introduction

 

Listener, écouteur en français... mais à quoi servent-il donc? à écouter, certes mais quoi? Des événements.

Les listeners vont capter un événement puis exécuter une callBack (fonction).

Un joueur ouvre son inventaire, l'événement InventoryOpenEvent va être dispatché et les listeners qui écouteront cet événement vont le capter et exécuter leurs fonctions.

 

II - Pourquoi utiliser des listeners && events

 

Imaginons que je programme un plateformer... mon joueur meurt, je pourrais lui faire appeler une fonction public de mon HUD pour changer le nombre de vies restantes.

Cependant, imaginons que plus tard je veuille que tout les ennemis aux alentours se réjouissent de la mort de mon héros. Je vais être contraint de rajouter cela sur mon joueur et de rajouter des fonctions publiques sur mes ennemis. Est-ce vraiment à un élément de mon jeu d'appliquer à tout les autres éléments les comportements qu'ils devraient avoir? Non, c'est plutôt à eux d'observer // d'écouter un événement PlayerDieEvent et de changer leurs animations en conséquence.

 

En supplément, utiliser un événement permet de simplifier le travail en équipe.

Je m'occupe du Player, JeremGamer des ennemis, mon player meurt:

- Dans le cas où je décide d'appeler une fonction publics des ennemis : "Hey jerem', tu pourrais rajouter cette fonction publique?"

- Dans le cas d'un événement, JeremGamer rajoute un listener et fait ce qu'il veut avec.

 

Cela permet d'avoir plus de liberté en séparant mieux les causes et les conséquences.

 

III - En pratique, les listeners

 

Avec bukkit, il faut créer sa propre class Listener qui va écouter un ou plusieurs événements et attacher cette class au pluginManager.

On commence par la création de la classe:

 
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class MonSuperListener implements Listener { 
}

 

La méthode pour attacher la class au pluginManager prend en paramètre un Listener d'où la nécessité d'implémenter Listener à votre Listener

 

Pour l'exemple, nous allons envoyer un message au joueur quand il se connecte.

 
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class MonSuperListener implements Listener {
    @EventHandler
    public void onPlayerConnection(PlayerJoinEvent event){        
        event.getPlayer().sendMessage("Bienvenue à toi! :D");    
    }
}

 

@EventHandler est une annotation permettant de signaler la fonction qui suit comme étant une callBack de listener. Le type d'event passé en paramètre déterminera quel événement est écouté. Le nom de la fonction importe peu, toujours est-il qu'il est préférable qu'elle soit compréhensible.

La documentation vous permettra de trouver l’événement qui correspond à ce que vous souhaitez écouter.

Ce même objet passé en paramètre contient différent informations et méthodes pour les récupérer. Certains événements peuvent être annulé par exemple BlockBreakEvent.

 
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class MonSuperListener implements Listener {    
    @EventHandler    
    public void onBlockBreak(BlockBreakEvent event){        
        event.setCancelled(true);    
    }
}

 

Le joueur va casser un block, l'évènement va être dispatché, intercepté par le listener, mon callBack va annuler l’événement et donc le bloc va ré-apparaître.

 

Faites attention, là j'écris qu'une seule fonction à chaque fois, mais je peux en mettre plusieurs, par contre il doit y avoir toujours @EventHandler devant chaque fonction. Sinon elles ne seront pas prise en compte comme callback.

 

Maintenant que l'on a créé un belle classe Listener, il va falloir l'ajouter au plugin manager, sinon, ça ne risque pas de fonctionner.

Pour ce faire, on récupère le pluginManager, on lui assigne le listener et on lui passe en second paramètre le plugin qui ajoute ce listener:

 
monInstanceDePlugin.getServer().getPluginManager().registerEvents(monInstanceDeListener, monInstanceDePlugin);

// - ou -

Bukkit.getPluginManager().registerEvents(monInstanceDeListener, monInstanceDePlugin);

// - ou -

Bukkit.getServer().getPluginManager().registerEvents(monInstanceDeListener, monInstanceDePlugin);

 

J'aurais tendance à conseiller le 2ème, le chemin de fonctions étant le plus cours.

 

Vous pouvez également supprimer un Listener

 
HandlerList.unregisterAll(monInstanceDeListener);

 

ou supprimer l'écoute d'un événement particulier sur un listener;

 
PlayerJoinEvent.getHandlerList().unregister(monInstanceDeListener);

 

Attention! L'instance doit être la même entre l'ajout et la suppression.

 

Il est bon de savoir que l'annotation @EventHandler peut prendre un paramètre: la priorité:

il y a 6 étapes:

  • EventPriority.LOWEST
  • EventPriority.LOW
  • EventPriority.NORMAL
  • EventPriority.HIGH
  • EventPriority.HIGHEST
  • EventPriority.MONITOR

 

 

Lorsqu'un évènement survient, Craftbukkit appelle les méthodes d'écoute qui ont une faible priorité en premier, et celle qui ont une forte priorité en dernier, afin que ces dernières aient le dernier mot sur le sort de l'évènement (notamment s'il est annulé ou pas). Par défaut la priorité est NORMAL.

 

Attention : La priorité MONITOR existe pour de l'observation, il ne faut pas l'utiliser pour avoir le dernier mot sur une modification//annulation d'événement.

Pour déclarer une propriété, au lieu d'écrire:

 
@EventHandler

 

on écrit:

 
@EventHandler(priority = EventPriority.HIGH)

 

Bien entendu, vous choisissez la propriété qui vous convient et vous utilisé une propriété seulement si vous en avez besoin. (Plusieurs plugins qui écoutent le même événement)

 

IV - Création d'événements

 

Vous pouvez également ajouter vos propres événements, pour cela, il faut créer sa classe qui étends Event.

 
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; 

public class MonSuperEvent extends Event {    
    private static final HandlerList handlers = new HandlerList();     
  
    @Override    
    public HandlerList getHandlers() {        
        return handlers;    
    }     
  
    public static HandlerList getHandlerList() {        
        return handlers;    
    }
}

 

Attention, il faut conserver ces méthodes et cette propriété. C'est elle qui contient tout les listeners.

Je peux rajouter d'autres méthodes et d'autres propriétés.

Je peux aussi implémenter Cancellable qui m'obligera à implémenter les méthodes

 
public boolean isCancelled()
public void setCancelled(boolean cancel)

 

J'aurais alors à les compléter pour les rendre fonctionnelles

 
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; 

public class MonSuperEvent extends Event implements Cancellable{    
    private static final HandlerList handlers = new HandlerList();     

    @Override    
    public HandlerList getHandlers() {        
        return handlers;    
    }     

    public static HandlerList getHandlerList() {        
        return handlers;    
    }    

    public boolean isCancelled() {       
        return cancelled;    
    }     

    public void setCancelled(boolean cancel) {       
        this.cancelled = cancel;    
    }
}

 

Maintenant que j'ai créé mon Event, je peux tout à fait l'écouter comme un Evenement classique. Cependant, faut-il encore le dispatcher pour que mes Listener reçoivent l'événement.

Pour ce faire:

 
MonSuperEvent event = new MonSuperEvent();
Bukkit.getPluginManager().callEvent(event); 
//Je dispatche l'événement, tout mes listeners qui écoute l'évènement le captent et agissent éventuellement dessus, décident éventuellement de l'annuler...

if (event.isCancelled()){    
// Par conséquent, si mon évènement est annulé, J'annule l'action.
}

 

Rien ne m'empêche de rajouter des paramètres au constructeur de l’événement pour y ajouter des informations.

Je pourrais créer l'événement TeamWinEvent, et avoir des méthodes .getTeam() ou .getPlayersTeam() qui me renvoient des informations importantes et qui sont intéressantes.

 

V - Une chose à savoir mais pas forcément à faire

Au lieu de créer une class pour votre listener, vous pouvez directement implémenter Listener à votre plugin et utiliser votre propre classe plugin comme listener

 
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class MonSuperPlugin extends JavaPlugin implements Listener {    
    @Override    
    public void onEnable(){        
        getServer().getPluginManager().registerEvents(this, this);    
    }    

    @EventHandler    
    public void onPlayerJoin(PlayerJoinEvent event){        
        event.getPlayer().sendMessage("Koukou twa ! :D");    
    }
}

 

C'est bien si c'est juste pour tester quelque chose rapidement... mais ça devient vite le foutoir sur votre classe principale.

 

Résumé:

  • Événements et listeners offrent une plus grande liberté d'action en séparant cause et conséquences.
  • Une fois notre classe listener créée, on lui ajoute les méthodes d'écoutes sans oublier l'annotation @EventHandler et on enregistre notre listener auprès du PluginManager
  • Possibilité d'annuler certains événements : setCancelled(true);
  • Possibilité de créer ses propres événements, de les dispatcher quand on veut et de les écouter comme les événements classiques

 

JeremGamer a également créé des cours sur les évènements et les listeners relatifs aux guis pour javax.swing et java.awt.

http://www.bukkit.fr/blog/3/entry-61-chapitre-28-les-%C3%A9v%C3%A8nements-partie-1/

http://www.bukkit.fr/blog/3/entry-62-chapitre-29-les-%C3%A9v%C3%A8nements-partie-2/

 

ilicos

SystemGlitch

blog-0476194001431870684.png

Il est sûr et certain que vous rencontrerez le cas suivant: vous avez un grand nombre de variables du même type à utiliser. Vous allez donc très certainement n'avoir qu'une idée en tête :

 
int nombre0 = 0;
int nombre1 = 1;
int nombre2 = 2;
int nombre3 = 3;
int nombre4 = 4;
int nombre5 = 5;

 

Vous conviendrez que ça ne semble pas très optimisé, et encore moins pratique. Il existe justement un outil pour faire cela ! C'est bien fait hein? ^_^

 

Cet outil, il s'agit des tableaux. Il permet, à l'aide d'une seule variable, de stocker une grand nombre d'éléments du même type. Nous prendrons ici int pour nos exemple. Mais sachez que les tableaux peuvent contenir absolument n'importe quel type, autant des types primitifs que des Objets (notion que vous verrez plus tard).

 

Vous savez ce qu'est un tableau en général, une multitude de cases alignées en lignes et en colonnes. Et bien ici, c'est pareil ! Dans chaque case, on aura une valeur. Pensez à toujours vous représenter vos tableaux dans votre tête lors de la création de vos programmes. ;)

 

Voyons désormais quelle forme cela prend en Java :

int tableau[];

 

La variable est déclarée mais pas initialisée pour l'instant, vous pouvez cependant le faire directement, nous verrons comment juste après.

Ici vous découvrez de nouveaux signes que sont les crochets. Ils caractérisent un tableau, leur utilité sera développée dans un instant.

 

Un tableau contient un certain nombre de cases, alors pour initialiser un tableau, soit on spécifie le nombre de cases (ces dernières seront alors vides et on pourra les remplir ensuite), soit on rempli directement à l'aide des valeurs choisies, et le nombre de cases sera alors adapté au nombre de valeurs entrées.

int tableau[] = new int[7]; //Tableau de 7 entiers, les cases sont vides pour l'instant

int tableau2[] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 }; //Aussi un tableau de 7 entiers, mais on rempli directement les cases

 

Notez l'utilisation d'un nouvel opérateur: new. Vous en aurez besoin sans arrêt plus tard, ne l'oubliez pas ! ;)

 

Rien de bien compliqué pour la première manière de faire. Pour la seconde, notez bien la syntaxe, le point virgule à la fin à ne pas oublier comme d'habitude, mais surtout les accolades: c'est entre ces dernières que se trouvera le contenu du tableau, chaque case étant séparée par une virgule.

 

La déclaration des valeurs se fait de la même manière que pour des variables classiques :

double tableau[] = { 0.0 , 0.7 , 1.5 , 4.2 };

char tableau2[] = { 'a' , 'b' , 'c' , 'd' };

String tableau3[] = { "BlaBla" , "Toto" , "Tata" , "Titi" };

 

Voici donc la forme de notre tableau dans notre tête :

 

REfdv3y.png

 

Vous allez me dire qu'il n'y a qu'une seule ligne ! Ce n'est pas vraiment un tableau alors! Et bien vous avez demandé à votre programme qu'il n'y ai qu'une seule ligne! En effet, le nombre de dimensions du tableau est définie par le nombre de crochets que l'on met! Ainsi, si vous voulez un tableau avec plusieurs lignes, rien de plus simple :

int tableau[][];

 

On aura désormais deux dimensions! En réalité, il s'agit d'un tableau contenant au minimum deux tableaux, mais cela revient au même. Vous devinerez donc qu'il ne sera pas plus compliqué d'initialiser ce genre de tableaux :

int tableau[][] = new int[7][3]; //Nombre de lignes, puis nombre de colonnes

int tableau2[][] = { { 0  , 1  , 2  , 3  , 4  , 5  , 6  }, //Contenu de la première ligne 
                     { 7  , 8  , 9  , 10 , 11 , 12 , 13 }, //Contenu de la deuxième ligne
                     { 14 , 15 , 16 , 17 , 18 , 19 , 20 } }; //Contenu de la troisième ligne

 

Pensez bien à ne pas oublier les deux accolades englobant la totalité des tableaux. Comme je vous le disais, on voit bien ici qu'il s'agit de plusieurs tableaux, car on retrouve les accolades, et les virgules entre chaque valeur. ;)

Vous devriez aussi apercevoir une espèce de tableau comme vous les connaissez, grâce au fait que j'ai bien aligné tous les nombres. :)

 

Sachez qu'il existe aussi les tableaux tridimensionnels, quadridimensionnels, etc...! Vous pouvez ajouter autant de dimensions que vous le voulez, mais au delà de trois cela devient très très abstrait et demande de bonnes connaissances en mathématiques (étudiées au supérieur).

 

C'est bien joli tout ça, mais vous ne savez toujours pas comment naviguer dans ces tableaux, comment récupérer les valeurs qu'il contient ni comment modifier ces valeurs. Vous naviguerez grâce à des index. Chaque case a sa position, comme pour une bataille navale, mais en ( x , y ) s'il s'agit d'un tableau bidimensionnel.

 

YEGXGoy.png

 

Notez bien que le premier indice est 0 ! En informatique, prenez le réflexe de compter en partant de 0 et non de 1, car c'est bien plus logique non? ^_^

 

Je pense qu'il n'est pas nécessaire de s'attarder plus longtemps sur les index, ce n'est pas bien compliqué à comprendre. Passons plutôt à la partie intéressante: naviguer dans son tableau.

 

Encore une fois ce n'est pas compliqué, les coordonnées seront insérées entre les crochets :

int tableau[] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 };

System.out.println(tableau[3]); //Affichera "4"
//Et oui n'oubliez pas que l'on compte à partir de zéro!
//Vous voyez désormais l'importance de bien faire attention à cela ;)

 

Dans le cas d'un tableau bidimensionnel, c'est le même principe :

int tableau[][] = { { 0  , 1  , 2  , 3  , 4  , 5  , 6  }, 
                    { 7  , 8  , 9  , 10 , 11 , 12 , 13 },
                    { 14 , 15 , 16 , 17 , 18 , 19 , 20 } };

System.out.println(tableau[2][1]); //Ligne puis colonne!
//Affichera "15"

 

Vous avez surement deviné comment modifier une valeur :

tableau[2] = 42; //La troisième valeur du tableau sera égale à 42

tableau2[1][4] = 7; //La cinquième valeur de la seconde ligne sera égale à 7

 

Pour finir, il est possible récupérer la taille d'un tableau, c'est à dire son nombre de cases :

tableau.length;

tableau2[2].length; //Dans le cas d'un tableau bidimensionnel
//Retournera le nombre de cases dans la troisième ligne

tableau2.length; //Retournera donc le nombre de lignes et non le nombre total de cases!

 

Faites bien attention, il s'agit bien du nombre de cases et non de l'index maximum ! D'ailleurs si jamais vous décidez d'aller chercher une valeur à l'aide d'un index plus grand que l'index de la dernière case, que se passera-t-il? :huh:

 

Une erreur, appelée exception sera lancée, il s'agit de ArrayIndexOutOfBoundsException, qui signifie tout simplement que vous allez chercher une valeur dans une case qui n'existe pas !

 

Voici un petit bout de code qui vous fera mijoter un moment ;) Des notions qu'il contient seront expliquées en détails dans le prochain chapitre.

int nombres[][] = { { 0 , 1 , 2 , 3 , 4 },
                    { 1 , 3 , 5 , 7 , 9 }};

 

for(int i = 0; i < 2; i++) {    

  for(int j = 0; j < 5; j++) {

    System.out.print(nombres[i][j]);       

  }

  System.out.println("");     

}

int nombres2[] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 };

for( int k : nombres2 )
     System.out.println(k);

 

Résumé :

  • Un tableau est un série de cases contenant des valeurs du même type.
  • Tous les types sont supportés, autant les types primitifs que les Objets.
  • Les tableaux multidimensionnels sont des tableaux contenant plusieurs tableaux.
  • Chaque valeur se trouve dans une case ayant ses coordonnées, sous forme d'index.
  • Le premier index est l'index 0 !
  • Vous pouvez récupérer le nombre de cases d'un tableau grâce à length.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch
Vous rencontrerez très souvent un cas de figure: Vous voulez effectuer la même action, ou des actions se ressemblant beaucoup un grand nombre de fois. Pour cela, il existe ce que l'on appelle les boucles ! C'est tout simplement une structure dont le contenu se répétera un nombre de fois donné ou indéterminé.
Je m'explique: Imaginez-vous dans une situation concrète. Votre programme permet de lister tous les patients enregistrés dans les fichiers d'un médecin. Vous ne savez donc pas combien de fois la boucle devra tourner. De plus, le nombre de patients pourra varier, on aura donc un nombre de tours indéterminé. On utilisera une boucle while.
Maintenant, imaginez que votre programme doit effectuer dix fois la même action par patient, à peu de chose près. Le nombre de tours est déterminé et ne changera pas au cours de l'exécution du programme. On utilisera donc une boucle for.
Commençons par la plus simple: la boucle while.
Il n'y a qu'a traduire depuis l'anglais pour comprendre son utilité: "Tant que" l'expression donnée en argument est vraie (une expression booléenne donc), les commandes spécifiées dans son bloc (accolades) seront exécutées en boucle.
Un exemple vaut mieux que de longues phrases :
  int i = 5; int j = 10; while( i < j ) { System.out.println(i + " < " + j); } //Bien sûr, de la même manière que les conditions, on peut ne pas utiliser les accolades s'il n'y a qu'une instruction à répéter.  
Ici, votre boucle écrira que i est inférieur à j tant que ce sera vrai.
Les plus malins auront repéré un gros problème.
En effet, ici, cette boucle tournera indéfiniment! Et oui! i et j ne changent pas de valeur donc i sera toujours inférieur à j et votre console sera remplie à jamais de texte! On appelle cela une boucle infinie. Il faut donc y faire très attention !
Pour éviter ce problème, nous incrémenterons i et constaterons le résultat. La boucle ne tournera que i - j fois soit 10 - 5 fois donc 5 fois. Essayez vous-même !
int i = 5; int j = 10; while( i < j ) { System.out.println(i + " < " + j); i++; }  
Le résultat donnera la figure suivante :
5 < 10 6 < 10 7 < 10 8 < 10 9 < 10  
Il existe une autre syntaxe pour cette boucle. Son fonctionnement est le même sauf que les instructions à exécuter se trouveront dans un bloc do. Cette forme porte le nom de "do-while".
int i = 5; int j = 10; do { //Instructions } while (i < j); //Notez bien qu'il y a un point virgule à la fin!  
Il y a cependant une petite différence avec sa sœur, la boucle while : la do-while s'exécutera au moins une fois, même si la condition n'est pas remplie !
Passons maintenant à la boucle for. Celle-ci vous donne la possibilité de lui spécifier combien de fois elle devra tourner. En plus de cela, elle a d'autres applications très intéressantes dont nous parlerons très, très bientôt !
Elle se syntaxe ainsi :
for(int i = 0 ; i < 10 ; i++) { //Instructions }  
Elle est un peu complexe par rapport à ce que nous avons vu jusque là, mais n'ayez crainte, elle est finalement simple d'utilisation. Il suffit de la connaître!
Lisons pas-à-pas cette ligne:
Le mot-clef for appelle une boucle. On ouvre les parenthèses pour ajouter les arguments. On déclare une variable avec la valeur choisie. Il est possible de lui assigner la valeur d'une variable déjà existante. Un point virgule? C'est là la particularité de cette boucle; les arguments sont séparés par des ";". Un test. Il revient exactement au même que pour la boucle while. Vous pouvez mettre une variable déjà existante en deuxième nombre: i < j par exemple. Et pour finir, l'actualisation. Ici c'est une incrémentation, mais il peut très bien s'agir d'une décrémentation ou d'un calcul classique comme i+=5. Attention cependant, vous ne pouvez utiliser que des opérations puis assignation dans ce champ !  
-----> Mais alors, cette boucle fait la même chose qu'une boucle while avec une incrémentation de la valeur testée?
 
On peut dire ça, cependant, elle reste plus pratique et optimisée !
int i = 0; while( i < 10 ) { System.out.println(i); i++; } //Revient au même que: for(int i = 0 ; i < 10 ; i++) System.out.println(i);  
La boucle for a une variante: la boucle for each. Elle permet d'utiliser l'intégralité des variables d'un tableau ou d'une collection.
C'est simple, "pour chaque valeur du tableau, effectuer telle action". Elle se syntaxe de la manière suivante :
int t[] = { 1 , 5 , 7 , 9 }; for(int i : t) { //Instructions }  
Je vais illustrer un peu cette notion plus compliquée à comprendre. Imaginez que chaque valeur du tableau est un client attendant à la caisse d'un supermarché. Chacun passe tour à tour et la caissière ré-exécute le passage des articles à chaque client. Quand il n'y aura plus de client, donc que le tableau aura déjà donné toutes ses valeurs, la caissière cessera de passer les articles.
Essayez quelque chose de très simple :
int t[] = { 1 , 5 , 7 , 9 }; for(int i : t) { System.out.println(i); }  
Vous constaterez que votre programme s'est contenté d'écrire un par un les nombres faisant partie de votre tableau puis, n'ayant plus rien à écrire, a stoppé la boucle.
Il est possible à tout moment d'utiliser break pour stopper la boucle. Cependant, les instructions qui suivent ce mot-clef dans le bloc ne pourront être effectuées !
while(i < j) { System.out.println(i); break; //La boucle ne s'exécutera qu'une seule fois car on la "casse" dès le premier tour! System.out.println("La boucle est stoppée"); //Erreur! Cette ligne se trouve après le break! }  
Vous pouvez utiliser un test :
while(i < j) { System.out.println(i); if(i == 8) break; System.out.println("Relancement de la boucle"); } //Ici on teste si i est égal à 8. Si c'est le cas, on stoppe la boucle sans dire qu'elle se relance!  
Maintenant, je vais vous expliquer une notion plus complexe.
Il est possible de mettre une boucle dans une boucle, elle-même dans une boucle. Comment gérer toutes ces boucles?
Tout d'abord, illustrons la situation :
 
 

 
Nous avons donc trois boucles, les unes dans les autres. Si vous ne souhaitez pas interrompre l'une d'entre elles depuis une autre, aucun souci à se faire. Sinon il va falloir s'accrocher !
Comme vous l'avez vu ici, j'ai identifié chaque boucle par une couleur. On peut faire à peu près la même chose en Java avec ce que l'on appelle les labels ! Il est donc possible de nommer une boucle au même titre qu'une variable !
label: while(i < j) { //Instructions }  
Dans notre situation, le code ressemblerait à cela (si on utilisait que des boucles for) :
vert : for(int i = 0 ; i < 10 ; i++) { //Instructions bleu: for(int j = 0 ; j < 10 ; j++) { //Instructions orange: for(int k = 0 ; k < 10 ; k++) { //Instructions } //Instructions après la boucle orange, dans la boucle bleu } //Instructions après la boucle bleu, dans la boucle verte } //C'est un bel exercice de compréhension des accolades n'est-ce pas?^^  
A l'intérieur de chacune de ces boucles sera alors possible de stopper sa boucle enfant ou sa boucle parent ! Il suffit d'utiliser le mot-clef break et le label de la boucle à stopper !
break vert;  
Et cela fonctionne également pour le mot-clef que nous allons voir dès maintenant !
continue permet de passer le reste des instructions de la boucle dans laquelle il se trouve puis de la relancer si le test renvoie toujours vrai, à condition qu'il ne précède aucun label. Par exemple, retournons à notre cabinet de médecine. Vous voulez déterminer combien il y a de femmes dans le total de vos patients. Nous utiliserons un tableau recensant vos patients, faire une boucle for pour tous les vérifier et déclarer une variable que l'on incrémentera si le patient est une femme.
int nombreDeFemmes = 0; String patients[] = {"homme" , "homme" , "femme" , "homme" , "femme" , "femme" , "femme" , "homme"}; int tours = patients.length; for(int i = 0 ; i < tours ; i++) { if(patients[i].equals("homme")) continue; nombreDeFemmes++; } System.out.println(nombreDeFemmes + " femmes consultent dans votre cabinet.");  
Votre programme vous retournera alors que 4 femmes consultent dans votre cabinet! On constate donc que l'incrémentation de nombreDeFemmes est passée si le patient est un homme !
Au passage, vous remarquerez que j'ai utilisé la méthode equals et non l'opérateur == pour tester la valeur du String. Je détaillerais ce qu'est une méthode plus tard, mais sachez qu'il est bien plus fiable de faire ceci pour le type String !
Si ce mot-clef est suivi d'un label, il s'occupera alors de passer les instructions de la boucle spécifiée, mais pas de la boucle dans laquelle il est inscrit !
vert: for(int i = 0 ; i < 10 ; i++) { while(/*un test*/) { if(i == 5) continue vert; } System.out.println(i); }  
Ici, notre boucle for aura pour objectif d'écrire la valeur de i à chaque tour. Nous faisons une autre boucle, peu importe le test, dans laquelle nous vérifions si i est égal à 5. Si c'est le cas, on empêchera la boucle "vert" d'écrire sa valeur.
Enfin, le mot-clef return stoppe lui aussi les boucles, mais ce dernier effectuant une action que nous détaillerons plus tard, je ne l'expliquerais pas tout de suite. Pour l'instant, pensez juste qu'il est l'équivalent de break.
Le code cadeau de cette semaine vous permet de compter le nombre de "p" dans une phrase ! Bien sûr, il est facilement modifiable afin que d'autres lettres puissent être vérifiées !
String phrase = "pourquoi, n'arrêtait pas de pester sa promise, pascal, " + "pourtant propriétaire depuis plusieurs années, partit dès la première" + " opportunité pour partager sa passion naissante avec son autre petite" + " copine, un pucelle de perpignan à la plastique parfaite, elle aurait" + " préféré le stopper plus tôt mais persuadée qu'il ne la planterait pas," + " elle n'eut plus que ses yeux pour pleurer, emprisonnée à perpétuité dans ses larmes."; int max = phrase.length(); int nombreDeP = 0; for (int i = 0; i < max; i++) { if (phrase.charAt(i) != 'p') continue; nombreDeP++; } System.out.println("Il y a " + nombreDeP + " \"p\" dans cette phrase.");  
Résumé :
Les boucles permettent d'effectuer plusieurs fois les mêmes actions, ou des actions quasiment identiques. Les boucles while tournent un nombre de fois indéterminé, tant que leur test retourne vrai, et peuvent aussi s'écrire en do-while. Il faut bien faire attention de ne pas écrire de boucle infinie! Sinon les actions se répéterons à l'infini, jusqu'à l’arrêt forcé du programme. Les boucles for tournent un nombre de fois déterminé et prennent en compte une variable dédiée, un test et une actualisation. Les boucles for each enregistrent dans une variable temporaire les valeurs d'un tableau ou d'une collection, une par une et tournent tant que ce tableau ou cette collection à d'autres éléments à proposer. Le mot-clef break est utilisé pour stopper les boucles, même si le test renvoie toujours vrai. Le mot-clef continue est utilisé pour passer les instructions qui restent avant que la boucle recommence. Il est quasi-identique à break, sauf qu'il relance la boucle. Les labels permettent de nommer ses boucles et de les gérer depuis d'autres boucles avec les mots-clef cités précédemment. Pour vérifier si une String est égal à une autre, utiliser la méthode equals(String).  
 
JeremGamer
 

SystemGlitch
Comme promis, je commence dès aujourd'hui à éclaircir les points noirs du chapitre précédent.
Nous allons parler aujourd'hui d'une notion extrêmement importante si ce n'est qu'un fondement même de la programmation orientée objet: Les méthodes.
 
Tout d'abord, une méthode est une série de commandes et d'actions étant effectuées lors de l'appel de cette dernière.
Tout cela peut vous paraître abstrait alors ne nous attardons pas plus, voici un exemple :
  void direBonjour() { System.out.println("Bonjour!"); System.out.println("Belle matinée n'est-ce pas?"); }  
Important: une méthode est un bloc à part ! C'est à dire que chaque méthode est comprise entre les accolades du bloc de la classe mais pas dans le bloc de la méthode principale !
public class Main { public static void main(String args[]) { //Instructions } void direBonjour() { System.out.println("Bonjour!"); System.out.println("Belle matinée n'est-ce pas?"); } }  
J'insiste bien sur ce point: les instructions contenues dans une méthode s'effectuent uniquement lors de l'appel de celle-ci ! C'est à dire que dans le code ci-dessus, le programme n'écrira pas "Bonjour!" ni "Belle journée n'est-ce pas?". Alors comment fait-on?
 
C'est très simple: il suffit d'écrire son nom !
 
Maintenant allons plus loin et créons nos propres méthodes !
Pour se faire, il faut d'abord que je vous explique un autre élément. Les méthodes retournent une valeur ou non. Cette valeur est comme une autre, d'un certain type et peut-être stockée dans une variable du même type. Ce type est donc spécifié lors de la déclaration de la méthode. Ici nous avons une méthode void, c'est une exception car elle ne retourne rien! Elle se contente juste d'effectuer les actions demandées.
 
Voici donc la déclaration d'une méthode retournant un int :
int getInt() { //... }  
On commence donc par le type, puis le nom de la méthode, que l'on utilisera alors pour l'appeler.
 
 
L'instant Convention
 
Le nom des méthodes doit commencer par une minuscule, et comme pour les classes et variables peut comporter des majuscules et nombres ou même des underscores ("_") pour remplacer les espaces. Comme toujours, utilisez des noms simples et descriptifs. Le nom des méthodes définissant quelque chose doivent commencer par "set". Le nom des méthodes retournant une valeur doivent commencer par "get". Si la valeur retournée est un boolean, alors le nom de la méthode doit commencer par "is". Quand il s'agit de: Ajouter quelque chose, alors le nom doit commencer par "add". Retirer quelque chose, alors le nom doit commencer par "remove". Convertir un type en un autre (on appelle cela un Cast), alors le nom doit commencer par "to".  
-----> Une méthode retourne une valeur, alors le mot-clef return évoqué dans le chapitre sur les boucles doit servir à cela non?
 
En effet, vous avez déjà rencontré le mot-clef qui entre ici en jeux! Il s'agit bien de return ! Commençons par le plus simple, les méthodes void. Dans ces méthodes, return n'est pas obligatoire. Cependant, si vous voulez arrêter la méthode à un certain endroit suivant une condition, vous pouvez l'utiliser au même titre que break !
 
Maintenant, voyons pour les méthodes retournant une valeur. Il est strictement obligatoire ! De la même manière que break, il stoppe la méthode, les instructions qui le suivent ne seront pas exécutées.
int getInt() { return 1; }  
Cette méthode est vraiment inutile mais elle est un bon exemple pour le point où nous en sommes. Cette méthode retournera 1 à chaque appel.
Petite précision: il est possible de mettre une variable à la place d'une valeur directe comme 1 ici, du moment qu'elle est du même type que celui spécifié dans la déclaration de la méthode.
 
Ainsi on peut stocker le retour de notre méthode dans une variable :
int i = getInt(); //i sera alors égal à 1 car getInt() retourne 1.  
Sachez que les méthodes retournant une valeur peuvent être appelées comme les void, mais la valeur retournée sera perdue.
int getInt() { System.out.println("Bonjour!"); return 1; }  
Une telle méthode pourra à la fois donner une valeur à une variable et écrire "Bonjour!" dans la console si elle est appelée de la même manière que juste au dessus! Cependant si on l'appelle "dans le vide", la valeur 1 sera perdue mais "Bonjour!" sera quand même écrit dans la console.
public static void main(String args[]) { getInt(); } //Ce programme sera parfaitement fonctionnel!  
Cependant s'il n'y a aucune utilité à ce que la méthode retourne une valeur, optez plutôt pour un void.
 
Afin qu'une méthode soit plus "modulable" et fonctionnelle pour plusieurs valeurs, nous ajouterons des paramètres en arguments. Un paramètre est une valeur à donner à la méthode lors de son appel. Cette dernière s'en servira alors pour effectuer ce qu'on lui demande. Exemple :
int multiplier(int nombre1 , int nombre2) { return nombre1 * nombre2; }  
La méthode multiplier contient ici deux paramètres. Lors de son appel, on pourra alors multiplier n'importe quels nombres entre eux !
int produit = multiplier( 5 , 2 ); //produit sera égal à 10  
Nous prenons ici l'exemple des int mais sachez qu'une méthode peut être d'absolument n'importe quel type, qu'il soit primitif ou non !
 
Note: Deux méthodes (ou plus) peuvent porter le même nom à condition qu'elles aient des paramètres différents. On appelle cela la surcharge de méthode.
int methode(int nombre1 , int nombre2) { return nombre1 * nombre2; } double methode(int nombre1 , int nombre2) { //Cette méthode ne sera pas valable! return nombre1 * nombre2; //Même si elle a un type différent, ses paramètres sont les mêmes } //que la méthode juste au dessus! void methode() { System.out.println("Une méthode"); } void methode(String message) { System.out.println(message); }  
L'utilisation des méthodes est très utile par exemple pour de longues suites d'actions répétées régulièrement et afin d’optimiser et de clarifier son code. Ainsi une simple ligne suffit à en exécuter de nombreuses autres !
 
Pour revenir aux points noirs du chapitre précédent: vous savez donc ce que vous avez fait en appelant la méthode nextLine() de votre variable sc de type Scanner !
 
Je vous offre en cadeau cette semaine un "pack" de méthodes permettant de coder une calculatrice !
double additionner(double i , double j) { return i + j; } double soustraire(double i , double j) { return i - j; } double multiplier(double i , double j) { return i * j; } double diviser(double i , double j) { return i / j; } double racine(double i) { return Math.sqrt(i); } double sinus(double i) { return Math.sin(i); } double cosinus(double i) { return Math.cos(i); } double tangente(double i) { return Math.tan(i); }  
 
Résumé:
 
Une méthode est une série d'actions s'effectuant lors de l'appel de cette dernière. Une méthode retourne une valeur du type spécifié dans sa déclaration. Cette valeur est retournée à l'aide du mot-clef return (obligatoire!). Les méthodes de type void ne retournent rien. Les valeurs retournées par une méthode peuvent être stockées dans une variable directement lors de l'appel de cette méthode. Une méthode peut avoir des paramètres. Ce sont des valeurs requises à l'exécution de la méthode. Plusieurs méthodes peuvent porter le même nom à condition qu'elles aient des paramètres différents. De nombreuses conventions sont appliquées aux méthodes. Veillez à vous en souvenir! Les méthodes optimisent et clarifient votre code.  
 
JeremGamer
 
 

SystemGlitch
Nous y voila! Votre premier TP! Il faudra rassembler vos connaissances pour y parvenir, à savoir :
Les variables et types primitifs, Les conditions et les opérateurs, Les tableaux.
Votre programme devra savoir faire les choses suivantes :
Un client entre dans le restaurant avec un budget aléatoire compris entre 10€ et 50€. On lui propose un plat approprié par rapport à son budget et par rapport à la "Carte du Chef". Cette carte devra comporter deux catégories. Le plat et son prix associé. Au moins quatre plats devront être proposés. Ne cherchez pas à faire de la haute gastronomie non plus et ne cherchez pas un bon rapport qualité/prix car nous ne sommes pas là pour ça ! Si l'envie vous chante, vous pouvez mettre un steak haché frites à 50€ !
Ne vous inquiétez pas, je vous guiderais un peu !
 

Tout d'abord, quelques petites choses plutôt utiles sont à savoir:
 
Premièrement, pour générer un nombre aléatoire, il vous faudra faire comme suit :
Random rand = new Random(); int budget = rand.nextInt((Max - Min) + 1) + Min; //Ici Max sera 50 et Min sera 10.
Deuxièmement, nous sommes dans le milieu du business ! Nous allons donc faire en sorte de prendre le prix du plat en prenant le prix le plus proche du budget du client, le plus élevé si possible. Il faut bien que les affaires tournent n'est-ce-pas?
 

Pour finir, il faut toujours penser avant d'agir! Réfléchissez bien à comment vous allez vous y prendre. Notez vos idées sur un papier ou un fichier texte et une fois que vous vous sentez prêt, allez-y ! Prenez votre temps, vous n'êtes pas chronométrés! Essayez de bien vous souvenir de toutes les petites astuces que l'on vous a donné jusque là.
 
Une dernière chose, sachez que ce TP repose en quasi totalité sur votre réflexion. Le code à effectuer est assez simple, il suffit juste de trouver le concept ! Encore une fois, prenez bien le temps de réfléchir, c'est la clef du succès de n'importe quel projet ! Si vous avez des questions pertinentes, de grosses difficultés ou bien fini votre TP, nous vous encourageons à nous contacter par message privé! Nous vous aiderons si besoin.
 
Maintenant, fini de rigoler, préparez vos neurones et vos claviers, c'est parti !
 
 
 
 
 
 
 
 
__________________________
 
 
 
 
 
Correction
 
 

Quand vous aurez terminé, prenez bien le temps de tester votre programme et de voir s'il est fonctionnel. Si c'est le cas, félicitations ! Vous êtes sur une excellente voie pour l'apprentissage de ce langage ! Cependant, si vous avez abandonné, ce n'est pas grave! On apprend de ses échecs. La correction est là pour ça ! Jetez-y donc un œil !
 
 
 
 

JeremGamer
 


SystemGlitch
Ce deuxième chapitre détaillera une notion qui parait évidente et qui pourtant, est plus subtile qu'on ne le pense. Seulement, une fois maîtrisée (très rapidement en général car encore une fois ce n'est que de la théorie), on l'utilise sans même y penser !
Je parle des opérateurs, tous ces signes, bien connu grâce aux mathématiques, qui permettent de "manier" les nombres.
Il en existe plein et vous en découvrirez sûrement certains. Certains sont liés aux notions dont j'ai parlé juste au dessus. Je détaillerais ça dans la seconde partie de ce chapitre.
Sans plus tarder, voici un tableau listant certains d'entre eux :
 
 
 
 
 
Ils ne sont pas tous présents ici. Cependant c'est déjà beaucoup ! Il est important de les connaître !
J'aimerais insister sur plusieurs points:
Incrémenter signifie "ajouter 1". L'incrémentation se fait donc ainsi : variable++;//Ou ++variable;  
Décrémenter signifie "retirer 1". La décrémentation se fait ainsi : variable--;//Ou --variable;  
Les additions se font ainsi : nombre3 = nombre1 + nombre2;  
Les soustractions, les multiplications, divisions et modulo de la même manière. Les opérations puis assignation se font ainsi : nombre += nombre2;//Équivalent à:nombre = nombre + nombre2;  
Les tests sont un peu différents, je vais vous détailler la syntaxe, mais vous ne saurez les utiliser que dans le prochain chapitre. Les tests s'emploient dans les structures de condition (prochain chapitre) et renvoient un boolean : nombre1 == nombre2; //Renvoie true si nombre1 a la même valeur que nombre2 nombre1 != nombre2; //Renvoie true si nombre1 n'a pas la même valeur que nombre2 nombre1 >= nombre2; //Renvoie true si nombre1 est supérieur ou égal à nombre2 //Etc...  
J'insiste sur la différence entre = et ==! Ces deux opérateurs n'ont absolument rien à voir! Il ne faut absolument pas les confondre! J'y reviendrais très bientôt dans la seconde partie. && et || s'utilisent eux aussi dans les structures de condition et renvoient eux aussi un boolean : nombre1 == nombre2 && nombre1 != nombre3 //On pourrait traduire par: "nombre1 est égal à nombre2 ET nombre1 n'est pas égal à nombre3" nombre1 == nombre2 || nombre1 < nombre2 //"nombre1 égal à nombre deux OU nombre1 inférieur à nombre2" Dans ce cas là mieux vaut utiliser =<  
&& est prioritaire par rapport à ||. Pour prévenir d'éventuels problèmes de lecture, ajoutez des parenthèses autour de vos "paquets", comme en mathématiques. Si il y a plusieurs opérateurs logiques, il faut obligatoirement les mettre ! (nombre1 == nombre2) && (nombre1 != nombre3) //Cela revient au même que la première condition donnée dans le bloc code juste au dessus. Mais c'est plus sécurisant! (nombre1 == nombre2 || nombre1 != nombre3) && (nombre2 > nombre3) //Cette condition est plus complexe//"nombre1 est égal à nombre2 OU nombre1 est différent de nombre3 ET nombre2 est supérieur à nombre3"  
L'opérateur ! ("NON logique") permet de récupérer l'inverse d'un boolean. boolean variable = true; boolean variable2 = !variable; //variable2 sera égal à false. //Attention! J'ai mis "= !variable" et non "!= variable"!  
L'opérateur = est sans doute le plus important et aussi celui qui recèle du plus de choses à savoir ! C'est donc ainsi que j'inaugure la seconde partie de se chapitre !  
Pensez bien que ce chapitre est théorique et que les 3éme, 4éme et 5éme bouts de code proposés ne sont pas fonctionnels! Ils doivent se trouver dans une structure de condition comme je l'ai précisé!
Prenons l'exemple du dernier bout de code. J'ai bien fait la distinction entre les deux opérateurs = et ==. Et pour cause ! L'un sert à assigner, l'autre à tester! Vous connaissez déjà tout à propos du testeur, alors plongeons-nous dans les détails de l'assignation.
boolean variable = true;  
Décortiquons ce bout de code... Tout d'abord, on a le type, le nom de la variable, l'opérateur et pour finir, la valeur de la variable. Vous savez déjà comment ça fonctionne. Allons plus loin !
On a bien un opérateur d'assignation ici! En fait, on assigne une valeur à la variable directement lors de sa déclaration !
 
-----> Tiens, ce mot me dit quelque chose !
 
C'est normal! Je l'ai bien fait rentrer dans vos esprits dans le chapitre précédent!
Déclarer une variable, c'est simplement "l'inscrire" dans votre code, lui réserver un emplacement dans le vaste monde de la mémoire ! Car oui chaque variable occupe un petit peu de place dans la mémoire de votre ordinateur !
Sachez qu'il est possible de déclarer une variable sans lui assigner de valeur !
int nombre;  
Il est toujours possible de changer la valeur d'une variable (non constante) après sa déclaration, du moment que cette dernière est accessible !
Dans ce cas, elle aura une valeur par défaut. Pour les variables numériques, ce sera "1", pour les boolean, ce sera false, et pour les autres types (que vous ne connaissez pas encore), ce sera null.
Hum, un nouveau mot-clef ! Vous le rencontrerez souvent, il signifie simplement "rien", le vide absolu. Quand une variable est null (dites "nulle"), c'est qu'elle n'a en fait aucune valeur, mais son emplacement est quand même réservé.
Il faut y faire attention car il a tendance à générer des erreurs assez facilement si on le manipule mal !
Voila! Ce chapitre est terminé ! Dans le prochain, on s'attaque au code! Vous allez pratiquer alors préparez vos claviers !
Et voici le bout de code promis ! Comme d'habitude, le minimum d'explications, il sera explicité dans le prochain chapitre.
//Ce code vous permet de savoir si un nombre est multiple d'un autre ou non! nombre1 = 6; nombre2 = 2; boolean isMultiple; if(nombre1 % nombre2 == 0) { isMultiple = true; } else { isMultiple = false; }  
Résumé :
Il existe de nombreux opérateurs permettant de manipuler vos variables numériques. Certains peuvent aussi permettre de modifier des variables d'autres types. Il existe beaucoup d'opérateurs permettant de tester vos variables. Ils retournent un boolean. Il faut tous les connaître ! Assigner une valeur à une variable revient à modifier sa valeur. Cela se fait par l'intermédiaire de l'opérateur "=". Déclarer une variable est nécessaire! Grâce à cela, un emplacement de mémoire lui est réservé, le programme connaît son type et son nom. Une variable peut être réassignée après sa déclaration, à n'importe quel moment, il faut seulement qu'elle soit accessible et non constante. Le mot-clef null équivaut à une valeur nulle, vide.  
 
JeremGamer
 

SystemGlitch
Bonjour à toutes et tous,
 
Lors du passage à la dernière version d'IPBoard il y a quelques mois, il y a eu un problème avec les balises codes et le format en général dans les blogs. Parmi ces problèmes, on peut retrouver la perte de police, ou les retours à la ligne dans les balises code. Cela rendait les cours parfaitement illisibles.
 
J'ai récemment reformaté l'intégralité des cours, des TPs et des Hors-série pour qu'ils soient de nouveau lisibles ! Cela a pris beaucoup de temps mais je pense que cela en vaut la peine.
 
Si vous n'avez pas encore commencé votre aventure dans le monde de Java, c'est le moment !
 
Votre prof de Java préféré,
JeremGamer
SystemGlitch
Dans ce chapitre, vous apprendrez à créer des barres d'outils, des listes sous forme graphique ainsi qu'à utiliser un explorateur de fichiers. Vous n'en serez pas surpris vu le titre du chapitre.
Commençons par les barres d'outils. Vous les connaissez bien et elles vous servent tous les jours! Ce sont en effet ces barres regroupant un certain nombres d'icônes cliquables qui permettent donc des actions rapides comme changer la police ou le gras, souligné, etc... dans les logiciels de traitement de texte! Nous utiliserons l'objet JToolBar. C'est très simple : le principe est d'y ajouter des JComponent comme les JButtons et d'indiquer où elle apparaitra. Car il faudra le spécifier lors de sa création.
  JToolBar toolBar = new JToolBar(); JToggleButton bold = new JToggleButton("<html><b>B</b></html>"); JToggleButton italic = new JToggleButton("<html><i>I</i></html>"); JToggleButton underlined = new JToggleButton("<html><u>U</u></html>"); JComboBox<String> font = new JComboBox<String>(); GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment(); //On récupère l'environnement graphique Font[] fonts = e.getAllFonts(); //Puis toutes les polices installées for (Font f : fonts) { font.addItem(f.getName()); //Afin de les ajouter dans le menu déroulant } toolBar.add(bold); toolBar.add(italic); toolBar.add(underlined); toolBar.addSeparator(); //On ajoute une barre de séparation toolBar.add(font); this.setLayout(new BorderLayout()); this.add(toolBar , BorderLayout.NORTH); //On positionne la barre en haut du panneau  

 
Certains auront remarqué que la barre d'outil est déplaçable à l'aide de la petite zone sur la gauche de la barre.
 

 
En effet il sera possible de la repositionner en bas du panneau, à sa gauche, à sa droite, etc... et même dans une petite boite de dialogue à part !
Il est possible de désactiver la possibilité de la déplacer ainsi :
toolBar.setFloatable(false);  
Vous aurez peut-être remarqué que les boutons ne se "surlignent" pas lors du survol de la souris. Il faut activer cette fonctionnalité qui ne l'est pas par défaut sur les barres d'outils :
toolBar.setRollover(true);  
Passons aux listes graphiques. L'objet que nous utiliserons s'appelle JList. La généricité est importante dans l'utilisation de cet objet. Nous utiliserons un argument générique de type String la plupart du temps. Mais attention, pour une fois c'est un tout petit peu plus compliqué car nous devrons passer par un autre objet, un modèle pour la liste : DefaultListModel.
JList<String> productList = new JList<String>(); DefaultListModel<String> data = new DefaultListModel<String>(); data.addElement("Aspirateur"); data.addElement("Moteur"); data.addElement("Tondeuse"); data.addElement("Machine à laver"); data.addElement("Fer à repasser"); data.addElement("Mixeur"); data.addElement("Micro-ondes"); data.addElement("Four"); data.addElement("Plaque de cuisson"); //... productList.setModel(data); //On applique le modèle à la liste  

 
Voila l'utilisation la plus basique d'une JList. Maintenant voyons les autres possibilités...
Pour commencer nous allons modifier les paramètres de sélection. Par défaut, la sélection est unique, voici comment gérer la sélection multiple :
productList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); //Sélection unique productList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); //Sélection multiple  

 
Vous devrez utiliser CTRL ou SHIFT pour effectuer une sélection multiple comme vous avez l'habitude de le faire avec n'importe quelle autre application.
Et si on changeait la disposition de notre liste?
productList.setLayoutOrientation(JList.VERTICAL); //Verticale, par défaut productList.setLayoutOrientation(JList.VERTICAL_WRAP); //Du haut vers le bas, puis affiche une autre colonne lorsqu'on a atteint le bas de la liste productList.setLayoutOrientation(JList.HORIZONTAL_WRAP); //Horizontale, de gauche à droite, puis retour à la ligne  
Le screenshot suivant montre le résultat avec JList.HORIZONTAL_WRAP :
 

 
Nous allons mettre notre liste dans un JScrollPane, puis définir le nombre d'items visibles à la fois :
productList.setVisibleRowCount(4); //Mettre -1 pour ne pas donner de limite, 8 par défaut JScrollPane listScroller = new JScrollPane(productList); this.add(listScroller);  

 
Vous pouvez aussi changer le layout complet des listes afin d'avoir un rendu complètement personnalisé. Attention, cette fois on utilisera des JComponent directement ajoutés à la liste, plus de modèle! Mais enfin, vous avouerez que c'est n'est pas très utile, autant utiliser un JPanel. Surtout que le layout ne fonctionne pas vraiment comme on l'espérait sur la liste :
JList<String> productList = new JList<String>(); productList.setLayout(new GridLayout(5,5)); productList.add(new JButton("Aspirateur")); productList.add(new JButton("Moteur")); productList.add(new JButton("Tondeuse")); productList.add(new JButton("Machine à laver")); productList.add(new JButton("Fer à repasser")); productList.add(new JButton("Mixeur")); productList.add(new JButton("Micro-ondes")); productList.add(new JButton("Four")); productList.add(new JButton("Plaque de cuisson")); //... JScrollPane listScroller = new JScrollPane(productList); this.add(listScroller);  

 
Pour faire cela correctement on utilisera plutôt un ListCellRenderer que vous devrez écrire vous-même! Celui par défaut n'affiche que des String.
Tout ce que vous avez à faire c'est créer une classe qui implémente ListCellRenderer puis appeler la méthode setCellRenderer() et donner une instance de cette classe en argument !
Voici un petit exemple pour pouvoir afficher des JLabel (ce sera votre code cadeau ) :
public class CustomCellRenderer extends JLabel implements ListCellRenderer<Object> { /** * */ private static final long serialVersionUID = -6145242041026326434L; private String[] texts = { "Allemagne" , "Belgique" , "France" , "Italie" }; private ImageIcon[] images = new ImageIcon[texts.length]; public CustomCellRenderer() { setOpaque(true); setVerticalAlignment(CENTER); for(int i = 0; i < texts.length ; i++) { images[i] = new ImageIcon(texts[i] + ".png"); images[i].setDescription(texts[i]); } } @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } ImageIcon icon = images[index]; String text = texts[index]; setIcon(icon); if (icon != null) { setText(text); setFont(list.getFont()); } return this; } }  
Et instanciez votre JList de cette manière :
JList countryList = new JList(new Integer[4]); //4 étant le nombre d'items CustomCellRenderer renderer = new CustomCellRenderer(); countryList.setCellRenderer(renderer);  

 
Il reste une chose à voir avec les JList. Comment les écouter? Nous utiliserons un MouseAdapter. Mais il faudra faire attention à plusieurs choses !
productList.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { //Nous allons effectuer quelque chose lors du double clic sur un item int index = productList.locationToIndex(e.getPoint()); //égal à -1 si l'utilisateur clique en dehors d'un item if (e.getClickCount() == 2 && index != -1) { //Double clic et on s'assure que l'utilisateur clique bien sur un item //... } else if (e.getClickCount() == 3 && index != -1) { //Supporter le triple clic //... } } });  
C'est tout pour les listes! Il y a bien d'autres choses à faire avec mais ce serait trop long de tout détailler alors je vous laisse vous renseigner par vous-même.
Pour finir ce chapitre, je vais vous présenter un outil très pratique, à votre disposition: j'ai nommé le JFileChooser.
Le principe est de faire choisir un fichier par l'utilisateur, vous en rencontrez souvent, par exemple lorsque vous faites "Enregistrer sous...". C'est très simple en soi, mais de nombreuses subtilités peuvent être ajoutées :
String path = null; JFileChooser chooser = new JFileChooser(); int option = chooser.showOpenDialog(null); if(option == JFileChooser.APPROVE_OPTION) { //Si l'utilisateur clique sur "Ouvrir" ou appuie sur Entrée path = chooser.getSelectedFile().getAbsolutePath(); System.out.println(path); }  

 
Une GUI toute faite ! Pourquoi s'en priver?
Sans plus tarder, voici les subtilités évoquées il y a un instant :
Tout d'abord, vous pouvez modifier ce qui est sélectionnable :
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); //Fichiers uniquement chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); //Dossiers uniquement chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); //Fichiers ou dossiers chooser.setMultiSelectionEnabled(true); //Pour activer les sélections multiples  
Ensuite, vous pouvez appliquer des filtres. C'est à dire que vous pouvez choisir les extensions de fichiers qui seront affichées. Avec le code suivant, seules les images seront affichées dans le sélecteur :
FileNameExtensionFilter filter = new FileNameExtensionFilter("Images", "jpg", "png", "gif", "jpeg" , "bmp"); //Le premier argument sera l'indication affichée à l'utilisateur chooser.setFileFilter(filter); chooser.setAcceptAllFileFilterUsed(false); //Permet d'empêcher l'utilisateur de retirer le filtre grâce au menu déroulant  
A l'aide de setCurrentDirectory(File dir), vous pouvez rediriger le sélecteur vers un dossier donné.
Sinon, vous pouvez définir dans quel dossier s'ouvrira le sélecteur. Pour cela il faudra directement le spécifier dans le constructeur :
JFileChooser chooser = new JFileChooser("C:\\Cours Java");  
 
Résumé :
 
Les JToolBar peuvent accueillir tous les JComponent. Les JList s'utilisent avec un modèle en général, du type DefaultListModel. Pensez à prévoir un JScrollPane pour les JList au cas où il y aurait beaucoup d'items. Les JFileChooser permettent à l'utilisateur de sélectionner un fichier.  
 
JeremGamer
 

SystemGlitch
Jusque là, vos GUI restent tout de même assez fades, nous allons y remédier !
Et en plus de cela, nous verrons aussi de nouveaux objets ouvrant la porte à de nouvelles possibilités, il s'agira essentiellement de conteneurs.
 
Pour commencer, nous allons créer des bordures de toutes sortes autour de nos panneaux !
Voici l'objet que nous allons utiliser: BorderFactory. Ce n'est pas bien compliqué alors je ne vais pas trop m'attarder sur les explications, jetez simplement un œil à l'exemple ci-dessous :
 
 
  private String[] list = { "Bevel Border", "Etched Border", "Line Border", "Matted Border", "Raised Bevel Border", "Title Border", "Compound Border" }; //C'est à cette partie qu'il faut s'intéresser private Border[] listBorder = { //Pour limiter le nombre de lignes de code BorderFactory.createRaisedBevelBorder(), BorderFactory.createTitledBorder("Titre"), BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.black, Color.red), BorderFactory.createEtchedBorder(Color.BLUE, Color.GRAY), BorderFactory.createLineBorder(Color.RED), BorderFactory.createMatteBorder(5, 2, 5, 2, Color.MAGENTA), BorderFactory.createCompoundBorder( BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.black, Color.blue), BorderFactory.createMatteBorder(5, 2, 5, 2, Color.MAGENTA)) }; public ExamplePanel() { for(int i = 0 ; i < list.length ; i++) { JPanel pan = new JPanel(); pan.setBorder(listBorder[i]); JLabel label = new JLabel(list[i]); label.setPreferredSize(new Dimension(150, 50)); label.setAlignmentX(JLabel.CENTER); label.setHorizontalAlignment(JLabel.CENTER); pan.add(label); this.add(pan); } }  

 
Ici j'ai décidé d'appliquer les bordures sur les panneaux, mais il est possible de les appliquer à n'importe quel composant héritant de JComponent, à l'aide de la même méthode setBorder(). Sachez également que vous pouvez choisir de donner null en argument afin de retirer les bordures.
 
Vous vous souvenez du texte qui ne s'affichait pas entièrement des les JTextArea lorsque ce dernier était trop long?
Sur les applications que vous utilisez, vous auriez vu apparaitre des barres d'ascenseurs sur le côté et en bas, appelées scroll bars. A notre tour de les mettre en place ! Nous utiliserons le conteneur JScrollPane et étudierons rapidement quelques unes de ses méthodes.
 
Il s'agit d'un conteneur, alors nous allons procéder de la même manière qu'avec les JPanel. Cependant, je vous conseille plutôt d'ajouter un JPanel au JScrollPane.
Pourquoi cela? Tout simplement car notre conteneur est dynamique et peut faire apparaitre ou disparaitre les scroll bars en fonction de la taille de ce qu'il contient. Si son contenu ne dépasse pas sa propre taille, alors les barres n'apparaitront pas! (Par défaut, il sera possible de les laisser affichées en permanence si vous le souhaitez. Nous verrons comment faire juste après.)
JTextArea text = new JTextArea(); JScrollPane scroll = new JScrollPane(text); this.setLayout(new BorderLayout()); this.add(scroll , BorderLayout.CENTER);  

 
Voici un exemple concernant les panneaux. Ce conteneur étant très adapté à l'utilisation du BoxLayout, il sera judicieux de l'utiliser même si la taille par défaut est suffisante pour tout afficher, afin de réaliser des GUI plus modulables pour l'utilisateur.
JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel , BoxLayout.LINE_AXIS)); for(int i = 0 ; i < 5 ; i++) panel.add(new JButton("Bouton n°" + i)); JScrollPane scroll = new JScrollPane(panel); scroll.setPreferredSize(new Dimension(300 , 50)); this.setLayout(new BorderLayout()); this.add(scroll , BorderLayout.CENTER);  

 
Comme je le disais plus haut, il est possible de changer la "politique d'affichage" des scroll bars. C'est tout simple :
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); //La barre verticale est toujours affichée scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); //Par défaut, affiche la barre si besoin scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); //N'affiche jamais la barre verticale scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); //La barre horizontale est toujours affichée scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); //Par défaut, affiche la barre si besoin scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); //N'affiche jamais la barre horizontale  
Les JTabbedPane vous permettrons, à l'image du CardLayout, d'empiler plusieurs panneaux. Cependant, l'utilisateur pourra facilement y accéder grâce aux onglets qui seront disposés en haut de l'écran!
C'est vraiment tout simple à utiliser :
JTabbedPane tabs = new JTabbedPane(); JPanel red = new JPanel(); red.setBackground(Color.RED); JPanel green = new JPanel(); green.setBackground(Color.GREEN); JPanel blue = new JPanel(); blue.setBackground(Color.BLUE); tabs.addTab("Rouge", red); tabs.addTab("Vert", green); tabs.addTab("Bleu", blue);  

 
Grâce à addTab(), vous pouvez ajouter des onglets; de la même manière, vous pouvez en retirer à l'aide de removeTab(int index)
Mais ça ne s'arrête pas là! Il y a pleins d'autres possibilités comme changer l'orientation de la barre d'onglets :
//A l'initialisation JTabbedPane tabs = new JTabbedPane(JTabbedPane.LEFT); //Affichera les onglets à gauche //ou avec la méthode tabs.setTabPlacement(JTabbedPane.RIGHT); //Affichera les onglets à droite tabs.setTabPlacement(JTabbedPane.BOTTOM); //Affichera les onglets en bas  

 
Avez-vous essayé de réduire la fenêtre pour voir ce qu'il se passait si les onglets occupent plus d'espace qu'ils en ont à leur disposition? Les onglets se redisposerons sur plusieurs lignes ou colonnes selon l'orientation. Il est possible de changer ce comportement pour le remplacer par l'ajout d'une petite flèche faisant défiler les onglets.
tabs.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); //Par défaut, redisposera les onglets sur plusieurs lignes si besoin tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); //Ajoutera un scroll si besoin  

 
Le screenshot ci-dessus utilise SCROLL_TAB_LAYOUT.
 
Et pour finir en beauté avec ces onglets, voyons comment insérer des icônes !
JTabbedPane tabs = new JTabbedPane(JTabbedPane.LEFT); JPanel red = new JPanel(); red.setBackground(Color.RED); JPanel green = new JPanel(); green.setBackground(Color.GREEN); JPanel blue = new JPanel(); blue.setBackground(Color.BLUE); tabs.addTab("Accueil", red); tabs.setIconAt(0, new ImageIcon("home.png")); tabs.addTab("Rechercher", green); tabs.setIconAt(1, new ImageIcon("search.png")); tabs.addTab("Informations", blue); tabs.setIconAt(2, new ImageIcon("infos.png"));  

 
Vous avez sans doute déjà rencontré des applications avec des fenêtres internes, comme bloquées dans la fenêtre principale. En Java, on combinera des JInternalWindow avec un JDesktopPane. Ce dernier est un conteneur du type bureau comme son nom l'indique. Le principe est simple une fois de plus : un bureau et des fenêtres ! Voici un petit exemple :
public ExamplePanel() { this.setPreferredSize(new Dimension(300 , 300)); JDesktopPane desktop = new JDesktopPane(); for(int i = 1 ; i < 6 ; i++) desktop.add(new CustomInternalFrame(i)); this.setLayout(new BorderLayout()); this.add(desktop , BorderLayout.CENTER); } class CustomInternalFrame extends JInternalFrame { CustomInternalFrame(int number) { this.setTitle("Fenêtre n°"+ number); this.setClosable(true); this.setResizable(true); this.setSize(150, 80); this.setVisible(true); } }  

 
Dans l'image, j'ai repositionné et redimensionné les cinq fenêtres, car à l'initialisation, elles apparaissent toutes au même endroit et à la même taille, vu que je n'ai pas pris le temps de m'occuper de ça dans le code. Il y a de nombreuses possibilités que je ne vais pas vous expliquer, je vous invite donc à jeter un œil à la documentation, vous êtes devriez commencer à avoir ce réflexe. Pour ce qui est d'habiller tout ça, vous savez déjà le faire car JDesktopPane hérite de JLayeredPane.
 
On enchaîne ! Voici désormais les JEditorPane ! Ces conteneurs permettent d'afficher du html, mais de façon un peu limitée tout de même. C'est largement suffisant pour du texte riche et des images comme dans cet exemple :
 

 
Oui vous saurez faire cela plus tard ! Sympa n'est-ce pas? Le panneau à droite contenant le texte est un JEditorPane contenant une mise en page html. Voici un exemple plus simple :
JEditorPane html = new JEditorPane(); JScrollPane scroll = new JScrollPane(html); html.setEditable(false); html.setEditorKit(new HTMLEditorKit()); html.setText("<html>Du <b>texte</b> <i>riche</i> en <u>HTML</u>.</html>"); this.setLayout(new BorderLayout()); this.add(scroll, BorderLayout.CENTER);  

 
Ce conteneur peut également se comporter comme un JTextArea si l'on n'utilisait pas la méthode setEditable(false), de même que le JTextPane que je vous invite à essayer rapidement.
 
Pour terminer ce chapitre, nous allons nous intéresser aux JSplitPane. Ils permettent de scinder votre conteneur en plusieurs "compartiments". Un exemple vaut mieux que de nombreux mots, je sais que vous reconnaitrez tout de suite de quoi il s'agit.
 

 
La barre de séparation peut être déplacée afin d'agrandir l'un des deux panneaux, et bien évidemment, de réduire l'autre.
Voici le code correspondant :
JPanel red = new JPanel(); red.setBackground(Color.RED); JPanel green = new JPanel(); green.setBackground(Color.GREEN); JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT , red , green);  
Rien de bien compliqué n'est-ce pas?
Comme d'habitude, nous n'allons pas nous arrêter là!
 
Tout d'abord, ici nous avons séparé horizontalement, à l'aide de JSplitPane.HORIZONTAL_SPLIT; pour séparer verticalement, on utilisera JSplitPane.VERTICAL_SPLIT.
Ici on a utilisé des JPanel, mais il peut s'agir de n'importe quel composant !
 
Nous allons améliorer notre barre de séparation et y ajouter deux petits boutons permettant de déplacer cette dernière à une extrémité de l'écran. Je conseille fortement d'essayer par vous-même car cela n'est pas illustrable par captures d'écran. C'est vraiment très simple :
split.setOneTouchExpandable(true);  

 
Maintenant nous allons définir la taille de la barre. Encore une fois, rien de plus simple :
split.setDividerSize(20); //20 pixels  

 
Et pour en finir avec cette barre, voici comment choisir sa position. J'imagine que vous avez remarqué que cette dernière apparait quasiment entièrement à gauche lors de l’initialisation. Nous allons la faire apparaitre de façon à ce que le panneau de gauche soit deux fois plus grand que celui de droite.
split.setDividerLocation(0.75); //De manière proportionelle, ici le panneau de gauche occupera 75% de l'espace //Ou en pixels si vous donnez un int  
Attention, cette méthode ne fonctionnera pas si vous l'appelez avant que la fenêtre soit affichée, c'est à dire qu'il faut que JFrame.setVisible(true) soit appelé avant l'appel de la méthode.
 
Vous vous êtes peut-être demandé : et si je voulais combiner les deux séparations en mettant plusieurs panneaux?
C'est possible bien-sûr ! Il suffit de combiner plusieurs JSplitPane !
JPanel red = new JPanel(); red.setBackground(Color.RED); JPanel green = new JPanel(); green.setBackground(Color.GREEN); JPanel blue = new JPanel(); blue.setBackground(Color.BLUE); JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT , red , green); JSplitPane split2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT , split , blue); contentPanel.setLayout(new BorderLayout()); contentPanel.add(split2, BorderLayout.CENTER);  

 
Nous allons nous arrêter là pour ce chapitre. Dans le prochain chapitre, nous nous pencherons sur les menus !
 
 
JeremGamer
 

SystemGlitch
Dans ce chapitre, je vous présenterai de nombreux nouveaux composants qui vous permettront d'étoffer vos GUI.
Il n'y aura que très peu d'explications car ce chapitre n'est absolument pas difficile, et le peu de notions que nous utiliserons dans ce dernier sont normalement déjà connues. Je vous invite fortement à essayer tous les bouts de code que je vous fournirai pendant ce chapitre.
 
Sans plus tarder, commençons avec quelque chose de très proche de ce que vous connaissez déjà : il s'agit d'un bouton restant enfoncé quand on clique dessus, j'ai nommé le JToggleButton.
  JToggleButton button = new JToggleButton("Désactivé"); button.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { JToggleButton b = (JToggleButton) e.getSource(); if(e.getStateChange() == ItemEvent.SELECTED) b.setText("Activé"); else if(e.getStateChange() == ItemEvent.DESELECTED) b.setText("Désactivé"); } });  
Rien de bien compliqué ! Seule nouveauté, j'utilise ici un ItemListener et non pas un ActionListener comme vous savez déjà le faire. J'aurais très bien pu le faire également de la manière suivante, mais le but est ici de vous montrer les différents moyens d'arriver au même résultat. Choisissez celui que vous préférez.
button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JToggleButton b = (JToggleButton) e.getSource(); if(b.isSelected()) b.setText("Activé"); else b.setText("Désactivé"); } });  
Le fonctionnement de ce bouton est finalement le même que celui des cases à cocher, que vous avez surement rencontré plus souvent dans l'utilisation courante. En Java, elles s'appellent les JCheckBox.
JCheckBox box = new JCheckBox("Cochez cette case"); box.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if(e.getStateChange() == ItemEvent.SELECTED) System.out.println("Coché"); else if(e.getStateChange() == ItemEvent.DESELECTED) System.out.println("Décoché"); } });  

 
 
 
Dans un tout autre style, le JLabel ne permet pas d’interaction native avec l'utilisateur. Il sera possible d'en ajouter manuellement cependant. Il s'agit de texte, tout simplement, mais contenu dans un composant Swing rigide, ce qui le rend bien plus pratique que d'écrire le texte grâce à l'objet Graphics.
JLabel label = new JLabel("Un JLabel.");  

 
Voici tout ce qu'il y a de plus simple n'est-ce pas? Les JLabel proposent de nombreuses options de personnalisation, ce qui va nous permettre de faire quelque chose d'un peu plus joli et adapté à nos envie. Tout d'abord, l'alignement peut être changé. Ici il est centré, mais on peut le caler à gauche ou à droite, etc... Ceci fonctionnera uniquement avec le BoxLayout.
JLabel label = new JLabel("Un JLabel." , JLabel.LEFT); //A gauche JLabel label2 = new JLabel("Un JLabel." , JLabel.RIGHT); //A droite //Ou à l'aide de cette méthode label.setHorizontalAlignment(JLabel.LEFT);  
On peut ensuite choisir la couleur et la police du texte :
label.setForeground(Color.RED); //Couleur du texte label.setFont(new Font("Impact" , Font.BOLD , 42)); //Police de caractère  

 
Les JLabel sont également utilisés pour afficher des images. Notre image aura donc les mêmes caractéristiques qu'un composant Swing! C'est une fois de plus un avantage dans certaines situations par rapport à l'utilisation de Graphics.
JLabel label = new JLabel(); try { label.setIcon(new ImageIcon(ImageIO.read(new File("image.png")))); } catch (IOException e) { e.printStackTrace(); }  

 
Il est bien-sûr possible d'afficher du texte à côté de l'image. Vous savez déjà comment vous occuper du texte avec les JLabel.
Si vous voulez changer le texte après avoir instancié votre label, vous pouvez utiliser la méthode setText().
 
 
 
Passons à un autre composant : le JComboBox. Vous le connaissez peut-être plutôt sous le nom de menu déroulant.
Nous allons pouvoir ajouter autant d'item que l'on souhaite, et mieux encore : programmer un comportement différent pour chaque item !
JComboBox<String> box = new JComboBox<String>(); //Attention à la généricité ! //On ajoute les items box.addItem("Item n°0"); box.addItem("Item n°1"); box.addItem("Item n°2"); box.addItem("Item n°3"); //Ou instancié à l'aide d'un tableau pour rentrer directement tous les items : String[] items = { "Item n°0" , "Item n°1" , "Item n°2" , "Item n°3"}; JComboBox<String> box2 = new JComboBox<String>(items);  

 
Maintenant, ajoutons un Listener pour rendre tout ça un peu plus dynamique ! Pour l'exemple, nous allons ajouter un bouton et un label. Lors du clic sur le bouton, on actualisera le label pour lui faire afficher le texte contenu dans l'item sélectionné.
JComboBox<String> box = new JComboBox<String>(); //Attention à la généricité ! //On ajoute les items box.addItem("Item n°0"); box.addItem("Item n°1"); box.addItem("Item n°2"); box.addItem("Item n°3"); JLabel label = new JLabel((String) box.getSelectedItem()); JButton button = new JButton("Actualiser"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { label.setText((String)box.getSelectedItem()); } });  
C'est bien, mais finalement on n'a pas écouté la JComboBox mais le JButton...
Retirons le bouton et actualisons le label automatiquement lorsque l'item sélectionné change :
JComboBox<String> box = new JComboBox<String>(); box.addItem("Item n°0"); box.addItem("Item n°1"); box.addItem("Item n°2"); box.addItem("Item n°3"); JLabel label = new JLabel((String) box.getSelectedItem()); box.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { label.setText((String)e.getItem()); } });  
Avec les JComboBox, il est préférable d'utiliser les ItemListener plutôt que les ActionListener, car je pense que vous avez compris qu'ils sont littéralement faits pour ça. Il est possible d'utiliser un ActionListener mais il s'avèrera moins pratique. A vous de choisir une fois de plus.
 
 
 
Dans la même catégorie du choix unique parmi plusieurs options, le JRadioButton est aussi très régulièrement utilisé.
Avant de vous montrer le code, je pense que vous présenter sa représentation pourra vous faciliter la compréhension du code qui suit.
 

ButtonGroup grp = new ButtonGroup(); //On créé un groupe dans lequel on ajoutera nos JRadioButton //Si on ne le fait pas, on pourra sélectionner les deux boutons en même temps //Or on souhaite qu'une seule option puisse être choisie JRadioButton rb1 = new JRadioButton("Option n°1"); JRadioButton rb2 = new JRadioButton("Option n°2"); grp.add(rb1); grp.add(rb2); panel.add(rb1); panel.add(rb2);  
Encore une fois ce n'est pas bien compliqué. Pour ce qui est des Listener, on utilisera des ActionListener en général, et on les appliquera aux boutons directement, et non au groupe.
 
Et maintenant, j'ai l'honneur de vous présenter les JSpinner !

Vous connaissez désormais leur nom !
La particularité de ceux-ci est que nous devons leur donner un modèle pour qu'ils sachent quoi afficher. Pour l'exemple, nous utiliserons un SpinnerNumberModel, qui permettra donc l'affichage de nombres. Sachez qu'il existe également les AbstractSpinnerModel, SpinnerDateModel et les SpinnerListModel. Je vous invite à jeter un œil à la documentation sur ces modèles afin de savoir tous les maitriser.
 
Pour réaliser le spinner présenté au dessus, le code est le suivant :
SpinnerModel model = new SpinnerNumberModel(0 , 0 , 100 , 1); //Valeur de base, minimum, maximum, palier JSpinner spin = new JSpinner(model); //On instancie le spinner avec notre model ((DefaultEditor) spin.getEditor()).getTextField().setEditable(false); //Optionnel, détails ci-dessous  
La dernière ligne permet de désactiver l'édition du champ de texte où est affichée la valeur par l'utilisateur. C'est bien-sûr optionnel.
 
Pour soulever les doutes possibles sur les paliers dans le modèle, voici une petite explication. Les paliers définiront quelle valeur sera ajoutée ou retirée lors du clic sur les flèches à droite du spinner. Si par exemple on a 0 en base et 2 en palier, quand on cliquera sur la flèche en haut (pour ajouter donc), le spinner affichera 2, puis 4, puis 6, etc...
 
 
Les JSlider sont des barres dont l'utilisateur peut déplacer le curseur. Elles sont notamment utilisées pour régler le volume sur les lecteurs multimédias.
 

 
Voici le code correspondant :
JLabel label = new JLabel("Valeur : " + 50); JSlider slider = new JSlider(); slider.setMinimum(0); //Valeur minimum slider.setMaximum(100); //Valeur maximum slider.setValue(50); //Valeur actuelle slider.setPaintTicks(true); //Afficher des graduations slider.setPaintLabels(true); //Afficher les valeurs sous les graduations supérieures //En unité du slider, c'est à dire par rapport aux valeurs minimum et maximum. slider.setMinorTickSpacing(10); //Espace entre les graduations inférieures slider.setMajorTickSpacing(20); //Espace entre les graduations supérieures slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { label.setText("Valeur : " + slider.getValue()); } }); panel.add(slider); panel.add(label);  
Il existe de nombreuses méthodes chez le JSlider, je vous suggère donc une fois de plus d'aller voir la documentation.
 
 
 
Pour finir ce chapitre, je vais vous présenter une jolie famille qui vous sera surement bien utile : les champs de saisie ! Nous en verrons quatre.
 
Le plus simple pour commencer: JTextField. Comme d'habitude c'est toujours très simple de les utiliser, cependant il faudra leur donner une taille préférée sinon ils seront tout petits dans les layouts n'imposant pas de contrainte de taille comme le FlowLayout.
JTextField field = new JTextField(); field.setPreferredSize(new Dimension(110 , 25)); JTextField field2 = new JTextField("Texte par défaut"); //Le texte sera présent directement dans le champ //Le champ de saisie prendra la taille adaptée par rapport au texte par défaut panel.add(field); panel.add(field2);  

 
Voila l'utilisation la plus simple que l'on peut en faire, mais il y a mieux ! Comme pour les JLabel, il est possible de changer la police et la couleur !
JTextField field = new JTextField(); field.setPreferredSize(new Dimension(110 , 25)); field.setForeground(Color.RED); //Couleur du texte field.setFont(new Font("Impact" , Font.PLAIN , 16)); //Police  

 
Grâce aux méthodes getText() et setText(), vous pouvez respectivement récupérer le contenu du champ de saisie ou alors le modifier. Il est possible de personnaliser le caret également (le trait clignotant). Une fois de plus, la documentation sera votre meilleur ami !
 
Il y a une autre spécificité aux champs de saisie: on écoutera ce qui s'y passe avec un CaretListener et non un ChangeListener.
Voici un exemple :
JLabel label = new JLabel(); JTextField field = new JTextField(); field.setPreferredSize(new Dimension(110 , 25)); field.addCaretListener(new CaretListener() { @Override public void caretUpdate(CaretEvent e) { label.setText(field.getText()); } }); panel.add(field); panel.add(label);  

 
Bien, vous savez à peu près tout sur les JTextField. Mais ses frères et sœur ont d'autres options à offrir ! Ce que je viens de vous apprendre reste bien évidemment valable pour eux.
 
Si vous avez essayé d'écrire un texte un petit peu plus long, vous avez remarqué qu'il n'y a pas de retour à la ligne, et que la fin n'est pas visible. Si l'on veut pouvoir faire des retours à la ligne, alors on devra utiliser des zones de texte bien plus grandes. On utilisera sa sœur, la JTextArea. Pour ce qui est du texte non visible, nous verrons cela dans le prochain chapitre.
JTextArea field = new JTextArea(); field.setPreferredSize(new Dimension(200 , 100));
 
Vous remarquerez qu'il n'y a plus de bordures, ce qui peut être un peu moche je vous l'accorde. Pas de souci, nous y remédierons dans un autre chapitre également.
Vous pouvez définir un retour à la ligne automatique :
field.setLineWrap(true); //Retour à la ligne activé field.setWrapStyleWord(true); //Empêche la coupure des mots  
Voyons désormais une autre variante: le JPasswordField. Il est identique au JTextField à la différence que seul le programme sera capable de lire se qu'il contient. Peu importe ce que vous écrirez, le même caractère s'affichera à chaque fois ('*' par défaut) :
 

 
JPasswordField pass = new JPasswordField(); pass.setPreferredSize(new Dimension(110 , 25)); pass.setEchoChar('•'); //Pour choisir le caractère à afficher  
Autre petite différence, il faut utiliser la méthode getPassword() et non getText() pour récupérer le contenu de la zone de saisie. Évidemment, il vous sera retourné ce que l'utilisateur aura vraiment tapé et non une multitude de points comme l'utilisateur peut le voir. Ça n'aurait aucun intérêt sinon...
 
Et pour clôturer ce long chapitre riche en contenu, le plus compliqué de champs de saisie: le JFormattedTextField. Ce dernier permet de filtrer ce que l'utilisateur entre, ou de limiter le nombre de caractères. Par exemple, on pourra choisir qu'il soit uniquement possible de rentrer des nombres. Ce qui le rendra bien plus pratique qu'un simple JTextField dans certains cas, et qui évitera de nombreux tests superflus.
 
Dans l'exemple je vais en empiler plusieurs à l'aide d'un BoxLayout, chacun aura un filtre différent :
JFormattedTextField ft1 = new JFormattedTextField(NumberFormat.getIntegerInstance()); //Uniquement des nombres entiers ft1.setPreferredSize(new Dimension(110 , 25)); JFormattedTextField ft2 = new JFormattedTextField(NumberFormat.getNumberInstance()); //Uniquement des nombres ft2.setPreferredSize(new Dimension(110 , 25)); JFormattedTextField ft3 = new JFormattedTextField(NumberFormat.getPercentInstance()); //Uniquement des pourcentages ft3.setPreferredSize(new Dimension(110 , 25)); JFormattedTextField ft4 = new JFormattedTextField(DateFormat.getDateInstance(DateFormat.SHORT)); //Uniquement des dates sous la forme jj/mm/aa ft4.setPreferredSize(new Dimension(110 , 25)); JFormattedTextField ft5 = new JFormattedTextField(DateFormat.getTimeInstance()); //Uniquement des heures sous la forme hh:mm:ss ft5.setPreferredSize(new Dimension(110 , 25)); panel.setLayout(new BoxLayout(this , BoxLayout.PAGE_AXIS)); panel.add(ft1); panel.add(ft2); panel.add(ft3); panel.add(ft4); panel.add(ft5);  

 
Il faut avouer que les possibilités des filtrage restent assez limitées. Alors voila ce que je vous propose: nous allons créer notre propre modèle ! Nous allons utiliser des MaskFormatter, adaptés aux numéros de série, de téléphone, etc...
MaskFormatter mask = new MaskFormatter(); try { mask.setMask("## ## ## ## ##"); //Numéro de téléphone français } catch (ParseException e) { e.printStackTrace(); } JFormattedTextField ft1 = new JFormattedTextField(mask); ft1.setPreferredSize(new Dimension(110 , 25));  

 
Je pense que cette histoire n'est pas très claire pour vous. Pourquoi avoir mis plein de dièses en argument du constructeur du MaskFormatter? Il s'agit de symboles représentant un ensemble de caractères. La dièse représente donc un nombre encore inconnu. Voici une liste détaillée:
# : Un chiffre ' : Un symbole comme '!' ou '$', appelé caractère d'échappement. Cela comprend aussi '\n' '\t'. U : Une lettre (les minuscules sont changées en majuscules) L : Une lettre (les majuscules sont changées en minuscules) A : Un chiffre ou une lettre ? : Une lettre * : N'importe quel caractère H : N'importe quel caractère hexadécimal (0-9, a-f ou A-F)  
 
Voici un exemple: Nous souhaitons que l'utilisateur entre un chiffre puis une lettre, puis n'importe quel caractère, on fera alors :
mask.setMask("# ? *"); //Les espaces sont facultatifs, mais s'ajouteront automatiquement lors de la saisie  
Il y a une autre manière de procéder : autoriser des caractères ou en interdire :
MaskFormatter mask = new MaskFormatter(); try { mask.setMask("***************"); //15 caractères max } catch (ParseException e) { e.printStackTrace(); } mask.setValidCharacters("abcdefghijklmnopqrstuvwxyz"); //Caractères autorisés mask.setInvalidCharacters("0123456789"); //Caractères interdits (dans le cas où on n'utilise pas setValidCharacters()) JFormattedTextField ft1 = new JFormattedTextField(mask); ft1.setPreferredSize(new Dimension(110 , 25));  
Problème : impossible de créer un masque dont le nombre de caractères est illimité.
Le code cadeau va donc porter là dessus ! Nous allons créer un KeyListener qui s'occupera de tester si le caractère est autorisé ou non :
package fr.bukkit.jeremgamer.cours; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JPanel; import javax.swing.JTextField; public class ContentPanel extends JPanel { /** * */ private static final long serialVersionUID = -7311158212818984619L; public ContentPanel() { JTextField field = new JTextField(); field.setPreferredSize(new Dimension(110 , 25)); TextFieldFormatter form = new TextFieldFormatter(); form.setValidCharacter("abcdefghijklmnopqrstuvwxyz/*-+&é\"\'(-è_çà)=~#{[|`\\^@]}²"); field.addKeyListener(form); this.add(field); } class TextFieldFormatter extends KeyAdapter { private String validCharacters = ""; private String invalidCharacters = ""; public void setValidCharacter(String s) { this.validCharacters = s; } public void setInvalidCharacters(String s) { this.invalidCharacters = s; } @Override public void keyTyped(KeyEvent e) { char c = e.getKeyChar(); if((c!=KeyEvent.VK_BACK_SPACE) && (c!=KeyEvent.VK_DELETE) && (c!=KeyEvent.VK_ENTER) && (c!=KeyEvent.VK_SPACE)) if(invalidCharacters.contains(Character.toString(c)) || !validCharacters.contains(Character.toString(c))) { Toolkit.getDefaultToolkit().beep(); //Notifier l'erreur par un son du système d'exploitation e.consume(); //On stoppe l'event, ainsi le caractère n'est pas ajouté } } } }  
Je pense que vous allez revenir ici régulièrement avant de tout maitriser, car il est difficile de tout retenir ! De plus je pense qu'un résumé ne sera pas nécessaire sachant que tout ce que je vous ai présenté ici était assez synthétique. Normalement vous êtes désormais en mesure de créer de belles interfaces graphiques interactives et dynamiques. Vous savez donc ce qui vous attend, un TP ! Cela faisait bien trop longtemps que je ne vous en avais pas fait faire.
 
 
JeremGamer
 

SystemGlitch
Nous entamons une toute nouvelle notion: la programmation évènementielle. Dans ce chapitre, vous ferez vos premiers pas dans ce domaine.
 
Il s'agira "d'écouter" ses composants, mais pas forcément, il peut également s'agir d'un objet comme les autres ! Cette fois-ci nous verrons simplement comment définir une action à réaliser lors de différents évènements possibles sur un bouton.
 
Le concept n'est pas bien compliqué :
Imaginez une situation peu probable : des enfants jouent dans leur chambre (ce sont nos objets), quand soudain leurs parents les appellent à table. Les enfants écoutent leurs parents et viennent déjeuner. Ici le principe est le même, nos objets écouteront d'autres objets, et quand ils se manifesteront, on définira un comportement.
 
Pour cela nous utiliserons des Listener. Il en existe plusieurs, qui sont tous des interfaces, et qui nous permettrons de prendre en compte plusieurs genres d'évènements. Par exemple on aura tout d'abord le plus fréquent : ActionListener, que l'on détaillera dans un instant, puis MouseListener, qui permet d'écouter la souris de l'utilisateur, ou KeyListener, qui permet quand à lui d'écouter le clavier.
 
Nous travaillerons sur de nombreuses classes anonymes, alors si vous ne vous sentez pas très à l'aise avec cette notion, je vous invite à relire le chapitre sur l'anonymat.
 
Reprenons une fenêtre toute simple, contenant simplement un bouton avec lequel nous allons interagir. Nous allons ensuite y ajouter un ActionListener.
 
Une seconde! Il s'agit bien d'une interface? Comment en faire une classe?
Souvenez-vous ! Il est possible de créer une classe anonyme à l'aide d'une interface.
 
Voici alors la forme que cela prendra :
JButton button = new JButton("Un bouton"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //Notre action à effectuer lors du clic System.out.println("Vous avez cliqué sur le bouton"); } }); Je vous invite à tester, on voit que la phrase s'affichera bien dans la console à chaque clic sur le bouton !
 
Vous voyez, rien de bien sorcier pour l'instant. C'est dans la seconde partie de ce chapitre que cela se compliquera, quand je vous expliquerai en détails comment cela fonctionne, et comment le réaliser sur vos propres objets.
 
Fermons la parenthèse, rien ne vous empêche d'ajouter plusieurs Listener à vos boutons, même s'il est préférable de tout gérer en un seul, ce qui est bien plus optimisé. Cependant s'il vous faut des Listener différents, pour écouter le clavier et la souris en même temps par exemple, alors il sera nécessaire d'en ajouter plusieurs, ce qui parait logique. Voyons un peu ce qui est réalisable à l'aide ce cet outil :
button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton source = (JButton) e.getSource(); //Pour récupérer le composant source, ici on est sûr qu'il s'agit d'un bouton donc le cast est sans risque source.setText("Bouton cliqué"); //Changer le texte du bouton lors du clic source.setEnabled(false); //Griser le bouton après le clic //Toutes les autres méthodes de JButton //N'oubliez pas que nous sommes dans une autre classe, faites bien attention à l'accessibilité des variables utilisées! } });  
Voyons désormais comment écouter le clavier de l'utilisateur. Nous allons implémenter KeyListener à notre panneau.
package fr.bukkit.jeremgamer.cours; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JPanel; public class ContentPanel extends JPanel implements KeyListener { /** * */ private static final long serialVersionUID = -7311158212818984619L; public ContentPanel() { this.addKeyListener(this); //Il faut penser à ajouter le Listener! this.setFocusable(true); this.requestFocus(); //Pour s'assurer que le panneau a bien le focus //Car les évènements seront déclenchés uniquement si le panneau a le focus } @Override public void keyPressed(KeyEvent e) { //Quand une touche sera enfoncée System.out.println("Key pressed: " + e.getKeyChar()); } @Override public void keyReleased(KeyEvent e) { //Quand une touche sera relâchée System.out.println("Key released: " + e.getKeyChar()); } @Override public void keyTyped(KeyEvent e) { //Quand une touche sera tapée (enfoncée puis relâchée) System.out.println("Key typed: " + e.getKeyChar()); } } Key pressed: b Key typed: b Key released: b Key pressed: u Key typed: u Key released: u Key pressed: k Key typed: k Key released: k  
On peut constater que KeyTyped() est appelé avant KeyReleased(), ce qui pourrait être pratique dans certain cas.
Avez-vous essayé les flèches directionnelles ou des touches comme Ctrl ou Alt? Le résultat est un peu décevant :
Key pressed: ? Key released: ? Key pressed: ? Key released: ?  
Sans parler du fait que KeyTyped() n'est pas appelé, on n'a le droit qu'à un point d'interrogation! Cela est du au fait que pour récupérer la touche pressée, on utilise la méthode getKeyChar(), qui retourne forcément un char, or ces touches pourraient difficilement retourner une telle valeur! On utilisera donc une autre méthode: getKeyCode(), qui retournera l'identifiant de la touche pressée.
Key pressed: 38 Key released: 38 //38: flèche haut Key pressed: 65 Key typed: 0 Key released: 65 //65: touche 'a'  
Hum tout de suite on est capables récupérer toutes les touches, mais tous ces numéros ne sont pas très pratiques... Comment s'y retrouver et les reconnaitre?
 
Miracle, l'API Java nous sauve la vie encore une fois ! La classe KeyEvent nous propose de nombreuses constantes statiques recensant toutes les touches de notre clavier !
 
Par exemple, si on veut faire un petit jeu 2D qui se joue avec les flèches du clavier, rien de plus simple maintenant que l'on a ces constantes !
int code = e.getKeyCode(); switch (code) { case KeyEvent.VK_LEFT: System.out.println("Aller à gauche"); break; case KeyEvent.VK_UP: System.out.println("Aller en haut"); break; case KeyEvent.VK_RIGHT: System.out.println("Aller à droite"); break; case KeyEvent.VK_DOWN: System.out.println("Aller en bas"); break; }  
Et voila un résultat plus convenable !
 
Vous avez sans doute remarqué que maintenant que nous utilisons getKeyCode(), KeyTyped() nous affirme toujours que la touche tapée porte le code 0. C'est parce que cette méthode est utilisée pour supporter toutes les touches. getKeyChar() sera surtout utilisé pour du traitement de texte.
 
Nous n'avons pas besoin de cette méthode, dans l'idéal, on aimerait bien pouvoir s'en débarrasser pour ne pas charger le code inutilement. Magie magie! Un autre merveilleux outil fournit pas notre API préférée est là pour nous ! N'est-ce pas génial?
On appelle cela les Adapter, ce sont des classes héritant des interfaces Listener, mais qui nous laissent le choix des méthodes utilisées. Comme il s'agit de classes, on devra alors procéder différemment :
package fr.bukkit.jeremgamer.cours; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JPanel; public class ContentPanel extends JPanel { /** * */ private static final long serialVersionUID = -7311158212818984619L; public ContentPanel() { this.addKeyListener(new KeyboardListener()); //Cette fois on instancie notre classe interne this.setFocusable(true); this.requestFocus(); } class KeyboardListener extends KeyAdapter { //Classe interne hériant de Keyadapter public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); switch (code) { case KeyEvent.VK_LEFT: System.out.println("Aller à gauche"); break; case KeyEvent.VK_UP: System.out.println("Aller en haut"); break; case KeyEvent.VK_RIGHT: System.out.println("Aller à droite"); break; case KeyEvent.VK_DOWN: System.out.println("Aller en bas"); break; } } //On s'est débarrassé de keyReleased() et keyTyped() } } package fr.bukkit.jeremgamer.cours; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JPanel; public class ContentPanel extends JPanel { /** * */ private static final long serialVersionUID = -7311158212818984619L; public ContentPanel() { this.addKeyListener(new KeyboardListener()); //Cette fois on instancie notre classe interne this.setFocusable(true); this.requestFocus(); } class KeyboardListener extends KeyAdapter { //Classe interne hériant de Keyadapter public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); switch (code) { case KeyEvent.VK_LEFT: System.out.println("Aller à gauche"); break; case KeyEvent.VK_UP: System.out.println("Aller en haut"); break; case KeyEvent.VK_RIGHT: System.out.println("Aller à droite"); break; case KeyEvent.VK_DOWN: System.out.println("Aller en bas"); break; } } //On s'est débarrassé de keyReleased() et keyTyped() } }  
Et maintenant, la souris ! Pour en tirer une application intéressante, nous allons utiliser le MouseListener sur une classe de bouton personnalisé ! Nous nous contenterons de changer la couleur de fond du bouton à titre d'exemple.
package fr.bukkit.jeremgamer.cours; import java.awt.Color; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JButton; public class CustomButton extends JButton implements MouseListener { /** * */ private static final long serialVersionUID = 7877964058790016354L; public CustomButton(String title) { super(title); this.addMouseListener(this); this.setBackground(Color.BLUE); this.setForeground(Color.white); } @Override public void mouseClicked(MouseEvent e) { //Lors du clic //On ne met rien ici car on s'occupe du changement de couleur dans //mousePressed() et mouseReleased() //On utilisera d'ailleurs un MouseAdapter en temps normal pour retirer cette méthode inutile. } @Override public void mouseEntered(MouseEvent e) { //Lorsque la souris entre dans la zone du bouton this.setBackground(Color.CYAN); this.setForeground(Color.black); } @Override public void mouseExited(MouseEvent e) { //Lorsque la souris sors de la zone du bouton this.setBackground(Color.BLUE); this.setForeground(Color.white); } @Override public void mousePressed(MouseEvent e) { //Lorsque que le bouton du clic est enfoncé this.setForeground(Color.BLACK); } @Override public void mouseReleased(MouseEvent e) { //Lorsque le bouton du clic est relâché this.setBackground(Color.CYAN); this.setForeground(Color.black); } }  
Je vous invite à essayer ce code
Je pense que vous avez compris le principe.
 
Dans la seconde partie de ce chapitre nous verrons plus en détails comment fonctionnent réellement les Listener, comment faire les votres, ainsi que comment écouter vos objets maison !
 
 
Résumé :
 
Les interfaces Listener permettent d'écouter des objets. C'est à dire être "informé" lorsqu'un évènement en relation avec cet objet ce produit. ActionListener permet d'écouter lorsqu'une action comme un clic sur un bouton est effectuée. KeyListener permet d'écouter le clavier. MouseListener permet d'écouter la souris. Les Adapter sont des classes implémentant les Listener et permettant de se passer des méthodes superflues.  
 
JeremGamer
 

SystemGlitch
Nous entrons désormais dans une autre dimension de la création de GUI: nous allons voir comment positionner des composants comme bon nous semble sur notre fenêtre, et surtout, les faire s'adapter automatiquement à la taille de la fenêtre ! Quand je parle de composants, je parle de boutons, de zones de textes, de barres de chargement, etc... Enfin nous y sommes !
 
La disposition de vos composants sera gérée par des Layout Managers, lesquels réagiront à plusieurs paramètres comme la taille du conteneur, le nombre de composants, etc... Ces layouts se trouvent tous dans le package java.awt.
 
Commençons par reprendre un panneau vierge. Nous allons créer une classe qui hérite de JPanel et nous l'ajouterons ensuite en tant que ContentPane à notre JFrame. Nous y ajouterons un bouton, tout ce qu'il y a de plus basique :
  public class ContentPanel extends JPanel { /** * */ private static final long serialVersionUID = -7311158212818984619L; private JButton button = new JButton("Un bouton"); //Intanciation de notre bouton public ContentPanel() { //A partir de là, tout fonctionne comme vous savez déjà le faire //On utilisera this car cette fois il fera directement référence à notre panneau this.add(button); //On ajoute le bouton, rien de bien compliqué! } }  

 
Et voila ! Un bouton, il ne fait rien, mais c'est un bon début !
Petite remarque, chaque composant est unique, c'est à dire que si vous tentez d'ajouter plusieurs fois notre objet "button", il ne s'affichera qu'une seule fois !
Vous remarquerez que notre bouton est centré, avec un petit écart entre lui et le bord supérieur du panneau, le layout par défaut à été utilisé car nous n'en avons défini aucun ! Il s'agit du FlowLayout. Ce layout est le plus basique: les composants se suivent en ligne, et dès qu'un composant atteint ou dépasse le bord du panneau, il y aura un "retour à la ligne". Exemple :
for(int i = 1 ; i <= 5 ; i++) this.add(new JButton("Bouton n°" + i));  

 
Si vous redimensionnez la fenêtre, vous verrez que les boutons se réorganisent par eux-même !
Des options sont disponibles, mais pour cela il faudra instancier manuellement le layout. Par exemple, dans l'exemple ci-dessous, j'augmente la taille des écarts entre les composants : (Cela est valable pour tous les autres layouts présentés plus tard dans ce chapitre)
FlowLayout flow = new FlowLayout(); flow.setHgap(15);//Écarts horizontaux flow.setVgap(15);//Écarts verticaux this.setLayout(flow); //On applique le layout à notre panneau for(int i = 1 ; i <= 5 ; i++) this.add(new JButton("Bouton n°" + i));  

 
 
Avec le FlowLayout, on peut également changer l'alignement. C'est à dire qu'au lieu de centrer, on peut faire partir de la gauche, de la droite, etc...
flow.setAlignment(FlowLayout.CENTER); //Centré, par défaut flow.setAlignment(FlowLayout.LEFT); //Commence à gauche flow.setAlignment(FlowLayout.RIGHT); //Commence à droite  
Avant de voir d'autres layouts, j'aimerais faire une petite parenthèse sur le positionnement absolu. Il s'agit simplement d'ajouter les composants sans layout. Je ne vous le recommande absolument pas, évitez-le le plus possible d'ailleurs, pour éviter de nombreux possibles problèmes de rendu, de chevauchements et autre. De plus utiliser cette technique revient à sacrifier le redimensionnement ainsi que l'adaptation automatique de la disposition. Bref, voici comment faire :
this.setLayout(null); //On dis à notre panneau d'utiliser le positionnement absolu button.setBounds(60, 25, 110, 27); //On positionne le bouton //x , y , largeur , hauteur this.add(button);  

 
 
 
BorderLayout :
 
Commençons par le plus simple à prendre en main: le BorderLayout. Ce layout permet de positionner facilement 5 composants ou conteneurs. (car oui l'enjeu sera de mélanger plusieurs panneaux par la suite, chacun ayant un layout différent, afin d'obtenir la disposition parfaite !)
Pourquoi seulement 5 composants? Une image vaut bien plus que des mots :
 

 
Le code permettant de réaliser cette disposition est le suivant :
this.setLayout(new BorderLayout()); //On indique qu'on utilisera le BorderLayout this.add(new JButton("NORTH") , BorderLayout.NORTH); //On ajoute un bouton dans la région supérieure this.add(new JButton("WEST") , BorderLayout.WEST); //De même dans la région gauche this.add(new JButton("CENTER") , BorderLayout.CENTER); //Centre this.add(new JButton("RIGHT") , BorderLayout.EAST); //Droite this.add(new JButton("SOUTH") , BorderLayout.SOUTH); //Région inférieure  
Petite remarque : Si vous n'ajoutez qu'un seul composant au centre, ce dernier prendra tout l'espace disponible. Cela peut-être assez utile.
 
Vous remarquerez que les composants sont redimensionnés. La majorité des layouts appliquent des contraintes à vos composants. Ici, NORTH et SOUTH prendront toute la largeur, WEST et EAST, le maximum de hauteur, et CENTER utilisera tout l'espace comme dit à l'instant.
 
Il est donc possible de prévoir certaines éventualités en donnant une taille préférée à vos composants. Il s'agira de la taille par défaut de votre composant. Je vais modifier cela sur le bouton WEST :
this.setLayout(new BorderLayout()); //On indique qu'on utilisera le BorderLayout JButton west = new JButton("WEST"); //On a besoin de l'instance du bouton pour définir les tailles west.setPreferredSize(new Dimension(200 , 50)); //largeur, hauteur comme d'habitude this.add(new JButton("NORTH") , BorderLayout.NORTH); this.add(west , BorderLayout.WEST); this.add(new JButton("CENTER") , BorderLayout.CENTER); this.add(new JButton("RIGHT") , BorderLayout.EAST); this.add(new JButton("SOUTH") , BorderLayout.SOUTH);  

 
Le bouton a bien sa taille préférée. Cependant, sa hauteur étant soumise à une contrainte du layout, celle-ci ne sera pas affectée par la taille préférée. Cela fonctionne de même pour NORTH et SOUTH bien-sûr mais avec la largeur soumise à une contrainte. Pour CENTER, les deux valeurs sont soumises à des contraintes, il est donc inutile de lui donner de taille préférée.
 
 
 
GridLayout :
 
Ce layout est très pratique pour créer des claviers virtuels de calculatrice par exemple. Il s'agit tout simplement de transformer votre conteneur en une grille dont toutes les cases auront la même taille. Ces cases se redimensionneront bien-sûr avec le panneau. Vous pouvez définir le nombre de colonnes et de lignes. Voici comment faire :
this.setLayout(new GridLayout(3 , 2)); //Lignes , colonnes for(int i = 0 ; i < 6 ; i++) this.add(new JButton(String.valueOf(i)));  

 
Vous pouvez changer le nombre de lignes et de colonnes via les méthodes setColumns(int n) pour les colonnes et setRows(int n) pour les lignes. (appelées via l'instance d'un objet GridLayout)
Vous avez vu que j'ai utilisé un boucle pour ajouter tous les boutons, et que ces derniers se sont positionnés comme il faut.
En effet, avec ce layout les composants "remplissent" petit à petit le conteneur, case par case. L'ordre est donc respecté.
 
 
 
BoxLayout :
 
Ce layout vous permettra de disposer vos composants en ligne ou en colonne. On aura donc deux attributs: LINE_AXIS et PAGE_AXIS. Exemple :
JPanel p1 = new JPanel(); p1.setLayout(new BoxLayout(p1, BoxLayout.LINE_AXIS)); //En ligne, le premier argument doit faire référence au conteneur auquel on applique le layout for(int i = 0 ; i < 6 ; i++) p1.add(new JButton(String.valueOf(i))); JPanel p2 = new JPanel(); p2.setLayout(new BoxLayout(p2, BoxLayout.PAGE_AXIS)); //En colonne for(int i = 0 ; i < 6 ; i++) p2.add(new JButton(String.valueOf(i))); this.add(p1); this.add(p2);  

 
Celui-ci est aussi très simple à utiliser vous en conviendrez. Là où j'ai un peu plus poussé la "difficulté", c'est que j'ai ajouté deux panneaux à mon conteneur (this). Vous devriez commencer à entrevoir ce que l'on va pouvoir faire en fusionnant plusieurs panneaux n'est-ce pas?
 
Il existe un conteneur proposé par le JDK qui vous permet de l'utiliser encore plus facilement ! Il s'agit de l'objet Box. Ce n'est rien d'autre qu'un panneau paramétré avec un BoxLayout. Voici comment l'utiliser pour avoir exactement le même rendu que ci-dessus :
Box p1 = Box.createHorizontalBox(); for(int i = 0 ; i < 6 ; i++) p1.add(new JButton(String.valueOf(i))); Box p2 = Box.createVerticalBox(); for(int i = 0 ; i < 6 ; i++) p2.add(new JButton(String.valueOf(i)));  
Dans le prochain chapitre, nous verrons d'autres layouts plus complexes, mais avec ce que vous venez de voir, vous pouvez déjà couvrir une infinité de possibilités !
 
 
Résumé :
 
La disposition des composants est gérée par des layout managers. Ces derniers imposent des contraintes à vos composants. FlowLayout est le layout par défaut. Il permet la disposition la plus basique avec un retour à la ligne automatique. BorderLayout permet une disposition simple et rapide par rapport aux bords du conteneur. GridLayout permet de disposer vos composants en grille, toutes les cases ayant la même taille. BoxLayout permet de disposer vos composants en ligne ou en colonne.  
 
JeremGamer
 

SystemGlitch
Avant de commencer à créer et positionner des composants comme des boutons, nous allons voir comment faire un peu de graphisme 2D ! Cette notion peut-être importante pour la suite car elle met au jour le principe par lequel l'intégralité du contenu de vos panneaux est affichée.
 
Pour commencer, il va falloir commencer à vous habituer à travailler avec de nombreuses classes, et donc bien vous organiser avec les packages. Ce que je vous conseille, c'est une classe par grand panneau et par fenêtre. Vous ne vous y retrouverez jamais sinon. Quand je dis par grand panneau, je veux dire qu'il n'est pas nécessaire de créer une classe pour un panneau ne contenant que 2 ou 3 composants et n'ayant pas d'application vraiment particulière dans le programme. Nous travaillerons donc avec des objets de votre création héritant des objets que nous avons vu jusque là. Des exemples seront présents un peu partout dans ce chapitre et ceux qui suivront.
 
Sachez donc que vos panneaux sont tous repeints un nombre incalculable de fois, et de manière événementielle. Nous verrons bientôt en quoi cela est "événementiel". Pour bien vous en faire prendre conscience, créons un petit programme avec une fenêtre et un panneau.
  public class MainFrame extends JFrame { /** * */ private static final long serialVersionUID = -5897453038271365972L; //Les composants Swing sont sérialisables public MainFrame() { this.setSize(300 , 120); this.setTitle("Ma première fenêtre"); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Ici on a très peu de choses à changer donc on utilise l'annonymat JPanel content = new JPanel() { /** * */ private static final long serialVersionUID = -3587004117783902958L; private int n = 0; public void paintComponent(Graphics g) { //La méthode qui nous intéresse super.paintComponent(g); n++; System.out.println("Panneau actualisé " + n + " fois."); } }; content.setBackground(Color.GREEN); this.setContentPane(content); this.setVisible(true); } }  
Si vous instanciez cette fenêtre, vous verrez que le panneau est très peu actualisé. Maintenant, redimensionnez la fenêtre.
Là vous pouvez voir que c'est assez impressionnant ! On atteint très rapidement les 1000 actualisations. Et encore, dites-vous qu'il n'y a aucun composant dans notre panneau ! Par exemple, vous voyez la barre clignotante dans les zones de saisie? Et bien le panneau est actualisé à chaque fois qu'elle apparait ou disparait, ainsi qu'à chaque fois qu'une lettre est ajoutée ou retirée.
Voila où je voulais en venir, votre panneau se repeint automatiquement. Vous aurez remarqué que je n'appelle jamais la méthode "paintComponent()" dans mon code, or cette dernière s'exécute quand même, et à chaque fois qu'une actualisation est nécessaire.
Enfin, la ligne la plus importante est celle où je fais appel à la super-méthode. Nous avons ici redéfini la méthode "paintComponent()", il est donc nécessaire de faire appel à la méthode de la super-classe pour que l'affichage se fasse correctement. Pourquoi? Simplement parce que c'est dans cette dernière que sont dessinés tous les composants et les éléments 2D. Sans ça, et bien vos composants n'apparaitraient pas! Si cela n'est pas clair, je vous invite à relire le cours sur l'héritage.
 
Pour peindre notre panneau, nous utiliserons non pas un pinceau mais l'objet Graphics, que l'on ne peut utiliser uniquement grâce à l'instance retournée par la méthode "getGraphics()" ou via le paramètre de la méthode "paintComponent()". (Cette méthode retournera null tant que la fenêtre n'aura pas été affichée au moins une fois avec "setVisible(true)")
 
Si vous voulez forcer l'actualisation de votre panneau, n’appelez pas "paintComponent()" mais "repaint()".
 
Passons au moment que vous attendiez : nous allons enfin commencer à dessiner ! Par contre, ne vous attendez pas à un rendu digne d'un professionnel de Photoshop, nous n'allons dessiner que des formes basiques, puis nous verrons que l'on peut écrire du texte, ainsi qu'afficher des images !
 
C'est parti ! Tout va se passer dans la méthode "paintComponent()" de notre panneau. Nous allons aussi retirer la ligne pour mettre le fond en vert, histoire de préserver l'intégrité de nos petits yeux.
 
Commençons par dessiner une forme ovale pleine.
public void paintComponent(Graphics g) { super.paintComponent(g); g.fillOval(getWidth() / 2 - 20, getHeight() / 2 - 20, 40, 40); //x , y , largeur , hauteur //On centre le cercle au milieu du panneau //On aura un disque vu que la largeur est égale à la hauteur }  

 
Pas mal non? Si vous redimensionnez la fenêtre, vous verrez que le disque reste bien centré. Gardez bien à l'esprit que les coordonnées sont croissante depuis le coin supérieur gauche du panneau, et que la coordonnée du disque est également celle du pixel supérieur gauche. C'est pour cela que quand je l'ai centré, j'ai bien récupéré la moitié de la taille du panneau, mais j'y ai également soustrait le rayon du disque. N'oubliez pas non plus que le pixel supérieur gauche du disque est celui du carré qui l'englobe !
 

 
Il est possible de dessiner des formes creuses, c'est à dire juste les contours. Pour cela, remplacez simplement "fill" par "draw" dans le nom de la méthode! Cela est valable pour les formes ovales mais aussi pour toutes les autres formes.
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(getWidth() / 2 - 20, getHeight() / 2 - 20, 40, 40); }  

 
Je pense que tout est expliqué, maintenant je vais vous faire une petite liste de ce qui est possible de faire avec ce merveilleux objet !
 
 
Dessiner des rectangles :
public void paintComponent(Graphics g) { super.paintComponent(g); g.fillRect(15, 15, 100, 50); g.drawRect(getWidth() - 115, 15, 100, 50); }  

 
 
 
Dessiner des rectangles avec des bords arrondis :
public void paintComponent(Graphics g) { super.paintComponent(g); g.fillRoundRect(15, 15, 100, 50, 10, 10); //Les deux derniers arguments définissent la taille de l'arc arrondi g.drawRoundRect(getWidth() - 115, 15, 100, 50, 10, 10); }  

 
 
 
Dessiner des lignes :
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawLine(0, 0, getWidth(), getHeight()); //x , y départ puis x , y arrivée g.drawLine(0, getHeight(), getWidth(), 0); g.drawLine(0, getHeight() / 2, getWidth() , getHeight() / 2); g.drawLine(getWidth() / 2 , 0 , getWidth() / 2 , getHeight()); }  

 
 
 
Dessiner à partir d'un tableau de bytes :
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawBytes("Bukkit.fr".getBytes(), 0, "Bukkit.fr".getBytes().length, 20, 20); }  

 
 
 
Dessiner un polygone :
private int x[] = {20, 30, 50, 60, 60, 50, 30, 20}; private int y[] = {30, 20, 20, 30, 50, 60, 60, 50}; public void paintComponent(Graphics g) { super.paintComponent(g); g.fillPolygon(x, y, 8); //Le dernier argument est le nombre de points }  

 
 
Et pour le plaisir, générons un polygone aléatoire ! Il se modifiera à chaque actualisation :
private Random rand = new Random(); public void paintComponent(Graphics g) { super.paintComponent(g); int x[] = {rand.nextInt((getWidth() - 0) + 1) , rand.nextInt((getWidth() - 0) + 1) , rand.nextInt((getWidth() - 0) + 1)}; int y[] = {rand.nextInt((getHeight() - 0) + 1) , rand.nextInt((getHeight() - 0) + 1) , rand.nextInt((getHeight() - 0) + 1)}; g.fillPolygon(x, y, 3); //Un triangle aléatoire sera dessiné }  

 
 
 
Dessiner un arc de cercle :
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawArc(20, 20, 100, 100, 0, 90); //x , y , largeur , hauteur , angle de début , angle de fin }  

 
 
 
Écrire du texte :
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawString("Vive Bukkit.fr", 20, getHeight() / 2); //Texte , x , y }  

 
 
 
C'est bien joli tout ça, mais si on est limité à dessine en gris, on ne va pas aller très loin... Voila donc comment faire pour personnaliser un peu tout ça !
public void paintComponent(Graphics g) { super.paintComponent(g); g.setFont(new Font("English157 BT" , Font.BOLD, 40)); //Nom de la police, style, taille g.setColor(Color.BLUE); g.drawString("Vive Bukkit.fr", 20, getHeight() / 2); }  

 
Nous sommes limité à cela avec l'objet Graphics, mais sachez que dans le prochain chapitre, nous verrons qu'il est possible d'aller bien plus loin, notamment choisir l'épaisseur des traits ou même faire des dégradés de couleur!
 
Il reste une dernière chose à vous montrer: comment afficher une image.
private BufferedImage img = ImageIO.read(new File("Java-duke-guitar.png")); public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(img, 0, 0, getWidth(), getHeight(), null); }  
N'oubliez pas d'englober tout ça avec un bloc try/catch IOException
 

 
Petit problème: Notre image est déformée... C'est logique vu qu'on lui a demandé de s'adapter à la taille de la fenêtre !
Si on avait une fenêtre de la même taille que l'image, ça donnerait ça :
 

 
Faites bien attention à ça! Alors voici quelques astuces :
 
Pour que l'image reste toujours à la bonne taille, faites comme suit :
private BufferedImage img = ImageIO.read(new File("Java-duke-guitar.png")); public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null); }  
C'est tout pour aujourd'hui ! Ce chapitre est déjà bien plus long que d'habitude !
En guise de code cadeau, je vais vous donner une petite astuce pour centrer votre image. Ainsi elle ne sera jamais déformée et sera toujours bien centrée, même en cas de redimensionnement !
 
(J'ai mis le fond du panneau en noir pour mieux voir les bords de l'image)
private BufferedImage img = ImageIO.read(new File("Java-duke-guitar.png")); public void paintComponent(Graphics g) { super.paintComponent(g); int imgHeight = img.getHeight() * getWidth() / img.getWidth(); int bHeight = (this.getHeight() - imgHeight) / 2; g.drawImage(img , 0 , bHeight, getWidth() , imgHeight , null); }  

 
Et comme je suis d'une générosité légendaire aujourd'hui, voici un autre code cadeau. Il sert à optimiser une image afin de l'afficher plus rapidement sur votre GUI, et donc optimiser votre programme. Car pour une raison inconnue, les images mettent assez longtemps à être traitées.
private BufferedImage optimizeImage(BufferedImage img) { GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); GraphicsConfiguration gc = gd.getDefaultConfiguration(); boolean istransparent = img.getColorModel().hasAlpha(); BufferedImage img2 = gc.createCompatibleImage(img.getWidth(), img.getHeight(), istransparent ? Transparency.BITMASK : Transparency.OPAQUE); Graphics2D g = img2.createGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); return img2; }  
Utilisez-le ainsi :
BufferedImage img = optimizeImage(ImageIO.read(file)); //En variable d'instance.  
Vous savez quoi faire par la suite !
 
Résumé :
 
Les panneaux sont repeints automatiquement et à chaque fois qu'ils ont besoin de l'être. Ils sont donc repeints de manière événementielle. L'objet Graphics nous permet de dessiner sur notre panneau. Cet objet n'est utilisable que via l'instance retournée avec la méthode "getGraphics()". Les modifications se font dans la méthode redéfinie "paintComponent(Graphics g)". N'oubliez pas de faire appel à la super-méthode sinon les autres composants ne s'afficheront pas! Vous pouvez dessiner toutes sortes de formes grâce à Graphics.  
 
JeremGamer
 

SystemGlitch
Jusque là vous travaillez sur une classe sans vraiment savoir ce que c'est. Nous allons y remédier !
Ce chapitre sera chargé en vocabulaire ! Il faudra bien le connaître !
 
Tout d'abord, désolé de me répéter, vous avez travaillé jusque là avec une seule classe, or en programmation orientée objet, vous allez en utiliser une multitude! Les perspicaces auront alors deviné ce qu'est un objet, terme que j'utilise déjà depuis un certain temps.
 
Grossièrement, un objet est une variable non primitive telle les String. Et oui les String sont des objets, bien qu'ils soient un peu à part, vous allez voir pourquoi.
Les objets sont donc des classes que nous avons instancié. Nous reviendrons là-dessus dans quelques instants. Avant ça décrivons un peu la structure d'une classe.
  class Main { //Déclaration de la classe avec le mot-clef "class" //Variables de classe et d'instance Main() { //Constructeur //Instructions lors de l'instanciation } void methode() { //Instructions } } //Fin de la classe  
N'ayez pas peur tout ce vocabulaire va vous être expliqué sous peu !
 
Je sais que maintenant vous ne devriez plus avoir trop de difficultés avec les accolades donc je vais passer cette partie.
 
Commençons par les variables de classe.
Les variables de classe sont des variables déclarées en dehors de toute méthode et dont on défini directement la valeur. Ainsi leur valeurs seront initialement les mêmes pour tout objet de cette classe instancié. Ces variables doivent être déclarées static.
 
Les variables d'instance sont déclarées sans valeurs, on les définira dans le constructeur. Ainsi les valeurs de ces variables pourront être différentes pour chaque objet de cette classe instancié.
 
Vous remarquerez que le constructeur est en quelque sorte une méthode mais sans type, il n'est même pas un void ! Le constructeur est la méthode appelée lors de l'instanciation de la classe grâce à l'opérateur new.
 
Avant que tout cela ressorte par vos oreilles, assemblons toutes ces notions car elles sont toutes liées !
Voici un petit exemple de classe :
class Main { static int nombre = 5; //Une variable de classe int nombre2; //Une variable d'instance Main(int n) { //Constructeur System.out.println("Instanciation de la classe Main"); this.nombre2 = n; } void methode() { //Instructions } } //Fin de la classe  
On instancie donc cette classe de la manière suivante :
Main obj = new Main(2); //La valeur mise en argument peut-être n'importe quel int  
Quand cette ligne sera lue par l'ordinateur, vous pourrez constater que "Instanciation de la classe Main" s'est écrit dans la console! Le contenu du constructeur s'est bien exécuté lors de l'instanciation de notre objet obj.
 
Vous avez surement remarqué un nouveau mot-clef : this. Il fait référence à l'objet courant. C'est à dire qu'avec lui, on peut accéder à toutes les variables et méthodes de la classe dans laquelle il se trouve à l'aide de l'opérateur ".". Ici on récupère nombre2. Il n'était pas nécessaire de mettre this cette fois-ci car le paramètre porte un autre nom. Imaginez la situation suivante :
int n; Main(int n) { n = n; }  
Comment votre programme sait à quelle variable vous faites référence? C'est là que this devient intéressant ! On rajoute this devant le premier n dans le constructeur et là on sait alors que l'on fait référence à la variable d'instance n et non au paramètre !
 
Ça va vous suivez? Je sais bien que ce n'est pas évident alors je le rappelle, vous pouvez m'envoyer des MP pour plus d'explications si vous le souhaitez !
 
Petite parenthèse : Les variables internes au constructeur ou aux méthodes sont appelées variables locales.
Main(int n) { this.n = n; int i = 6; //Une variable locale }  
Le ou les constructeurs d'une classe doivent porter le même nom que la classe.
 
Oui j'ai bien dit "les" constructeurs ! De la même manière que les méthodes, il est possible d'avoir plusieurs constructeurs, du moment qu'ils ont des paramètres différents !
class Main { Main() { System.out.println("Instanciation de la classe Main sans argument"); } Main(int n) { System.out.println("Instanciation de la classe Main avec l'argument " + n); } Main(int n , int j) { System.out.println("Instanciation de la classe Main avec les arguments " + n + " et " + j); } }  
Une telle classe pourrait donc s'instancier de trois manières différentes :
Main obj = new Main(); Main obj2 = new Main(5); Main obj3 = new Main(7 , 42);  
Une classe à un constructeur par défaut, qui ne fait rien et qui ne requiert aucun argument. C'est pour cela que faire ceci est inutile :
class Main { Main() {} }  
Si vous décidez de ne pas mettre de constructeur sans argument, alors il sera impossible d'instancier la classe sans argument.
class Main { Main(int i) { //Instructions } }  
Avec une telle classe, il sera impossible de faire ceci :
Main obj = new Main();  
La seule manière d'instancier cette classe sera alors de spécifier un int en argument.
 
Une classe est aussi un "casier" à méthodes. Les méthodes contenues dans une classe lui sont spécifiques. Il est donc impossible qu'une autre classe les utilise, à moins qu'elle dispose d'une instance de cette classe !
class Main { int i = 5; void methode() { System.out.println("Plop!") } }  
Notre classe Main ressemble à ça. Pour qu'une autre classe puisse l'utiliser, il faudra instancier la classe Main puis appeler la méthode en passant par l'objet et en utilisant l'opérateur ".". On peut aussi récupérer les variables de cette même classe.
Main obj = new Main(); obj.methode(); int j = obj.i;  
Il existe un moyen d'utiliser des méthodes issues d'une autre classe sans l'instancier, mais pour cela, il faut que cette méthode soit statique, désignée par le mot-clef static. Ces méthodes ne doivent en aucun cas utiliser des variables d'instances ! Tout simplement car on n'est pas certain que la classe qui la contient à été instanciée !
static void methode() { //Instructions }  
Cette méthode pourra être appelée de n'importe où à n'importe quel moment !
Attention cependant, l'utilisation des méthodes statiques peut-être dangereuse. Il faut les utiliser que dans des cas précis et ne pas en abuser car ils peuvent énormément fragiliser votre code, ce qui peut causer de nombreuses erreurs et bugs! Il faut donc être bien prudent quand à leur utilisation.
 
Ces méthodes sont essentiellement utilisées pour des actions simples ne faisant pas entrer de facteurs modifiant le fonctionnement du logiciel. Des utilitaires en général. Par exemple on souhaite faire une classe qui permet d'effectuer tout un tas d'actions concernant une liste. (Cette classe est déjà existante dans l'API Java) Toutes ses méthodes seront alors statiques !
 
Les méthodes statiques s'appellent différemment. On met d'abord le nom de la classe d'où elle provient, l'opérateur "." puis le nom de la méthode suivie des arguments.
//Méthode existante, justement pour trier une liste grâce à l'API Java Collections.sort(list); //La méthode sort est statique et issue de la classe Collections  
J'allais oublier ! Vous l'avez sûrement déjà oublié donc je vous le rappelle : voici comment créer une classe avec Eclipse :
 
Commencez par faire un clic droit dans l'explorateur à gauche à l'endroit où vous souhaitez créer la classe, puis "New -> Class".
 

 
Vous ne savez pas encore tout sur les classes mais je pense que vous avez eu votre dose pour aujourd'hui !
 
Nous ferons alors une petite pause avec les classes avant d'entamer des notions complexes qui y sont liées.
Vous pouvez être fiers de vous! Vous en savez beaucoup plus sur le jargon du développeur désormais !
 
Résumé:
 
Un objet est une instance d'une classe. On utilise donc une multitude de classes dans la programmation orientée objet. Les variables de classes sont déclarées et définies en dehors de toute méthode. Les variables d'instances sont déclarées en dehors de toute méthode et définies dans le constructeur lors de l'instanciation de la classe. Le constructeur est la méthode exécutée lors de l'instanciation de la classe. Il peut y avoir plusieurs constructeurs du moment qu'ils ont des paramètres différents. Une classe s'instancie à l'aide du mot-clef new. Pour pouvoir utiliser les méthodes d'une classe depuis une autre, on doit utiliser une instance de celle-ci. Les méthodes statiques peuvent être appelées depuis n'importe où et n'importe quand, sans avoir besoin d'instancier la classe qui la contient. Elles sont à utiliser avec précaution !  
 
JeremGamer
 
 

SystemGlitch
Jusque là, vos programmes n'étaient pas interactifs, et donc sans grand intérêt... Réjouissez-vous, nous abordons enfin la communication avec l'utilisateur !
 
Comme nous ne travaillons que sur console pour l'instant, je vous apprendrais à communiquer via du texte, tel un analyseur syntaxique.
 
Sachez que c'est très simple ! Il n'y a que quelques petites choses qui vous seront inconnues mais qui ne tarderons pas à être expliquées dans les chapitres à venir.
 
Nous allons utiliser une classe de l'API Java qui nous permet donc "d'établir la communication": la classe Scanner!
Je vous invite donc dès maintenant à ajouter ceci à votre méthode principale :
Scanner sc = new Scanner(System.in);  
Vous venez de déclarer une variable de type Scanner. Nous ne somme plus dans les types primitifs! Cette variable est ce que l'on appelle un Objet.
 
Remarque : C'est la première fois que nous utilisons une classe, et c'est un autre développeur qui l'a concoctée pour nous ! Nous allons devoir donc importer les ressources nécessaires dans notre code. Déclarez votre variable puis utilisez le raccourci clavier CTRL+SHIFT+O pour qu'Eclipse s'occupe de gérer automatiquement les imports. Sinon passez votre curseur sur le mot Scanner qui devrait être indiqué comme une erreur et votre IDE devrait vous proposer "Import 'Scanner' (java.util)".
 
Et maintenant, commençons à parler un peu avec notre utilisateur :
public static void main(String args[]) { Scanner sc = new Scanner(System.in); System.out.println("Comment vous appelez-vous?"); String nom = sc.nextLine(); System.out.println("Bonjour " + nom + "!"); sc.close(); }  
Ce programme va donc demander son nom à l'utilisateur puis le lui retransmettre. Le résultat Devrait ressembler à ça :
Comment vous appelez-vous? Robert <-------------- Entrée de l'utilisateur Bonjour Robert!  
Il y a plusieurs choses sur lesquelles il faut insister. Tout d'abord, notre objet sc est un flux. Cela signifie que comme un robinet, il faut le fermer après utilisation! C'est la dernière ligne: sc.close();
Tous les flux se ferment de la même manière, avec la méthode close().
 
Ensuite, vous remarquerez que le programme se fige temps que l'utilisateur n'a pas répondu à la question. Cela provient de la ligne sc.nextLine();. C'est elle qui s'occupe de récupérer la réponse de l'utilisateur, que l'on stocke alors dans une variable String. Une fois que l'utilisateur aura validé son entrée, le programme se poursuivra.
 
Ici nous utilisons une méthode permettant de récupérer une String. Sachez qu'il existe aussi des méthodes pour récupérer des types primitifs !
Scanner sc = new Scanner(System.in); int i = sc.nextInt(); short s = sc.nextShort(); double d = sc.nextDouble(); long l = sc.nextLong(); byte b = sc.nextByte(); float f = sc.nextFloat(); boolean bl = sc.nextBoolean(); //Attention il n'existe pas de méthode pour récupérer les char. Utilisez alors la méthode nextLine().  
Voila il n'y a rien d'autre à savoir pour l'instant sur l'utilisation de cette classe. Cependant il y a un conseil que j'aimerais vous donner par rapport à la communication avec l'utilisateur:
Règle d'or: Ne JAMAIS faire confiance à ce dernier! Effectuez toujours des tests pour voir ce qu'il fait! Par exemple s'il rentre une lettre au lieu d'un chiffre dans une calculatrice, ça ne risque pas de bien fonctionner!  
En application , soit vous fermez le logiciel, l'utilisateur n'avait qu'a répondre correctement, soit vous utilisez une boucle !
 
Si vous avez bien suivi au cours précédent, je pense que vous saurez quelle boucle utiliser !
 
Ajoutons quelques petites choses à notre programme. Maintenant que nous connaissons le nom de l'utilisateur, nous allons lui redemander et vérifier s'il se paie notre tête ou non.
public static void main(String args[]) { Scanner sc = new Scanner(System.in); System.out.println("Comment vous appelez-vous?"); String nom = sc.nextLine(); System.out.println("Bonjour " + nom + "!"); System.out.println("Pouvez-vous me répéter votre nom s'il vous plait?"); String confirmation = sc.nextLine(); while(!confirmation.equals(nom)) { System.out.println("Vous mentez! Répondez sérieusement!"); confirmation = sc.nextLine(); } System.out.println("Merci! Votre nom est bien " + confirmation + "."); sc.close(); }  
Vous pouvez tester! N'est-ce pas génial? Si cela vous amuse vous pouvez même ajouter des comportements différents en fonction du nombre de fois que l'utilisateur ne répond pas correctement !
 
Concentrons nous sur la boucle. On y teste si la variable confirmation est différente de nom récupéré juste avant.
Donc tant qu'elles ne seront pas égales, le programme redemandera à l'utilisateur de "répondre sérieusement" !
La boucle se termine enfin quand l'utilisateur réécrit exactement son nom, on peut alors lui dire merci et terminer le programme tout en pensant à bien fermer le flux de sc.
 
 
Résumé :
La classe Scanner permet de lire les entrées clavier. Il existe de nombreuses méthodes permettant de récupérer les différents types primitifs et les String. Les Scanners sont des flux, il faut les fermer après utilisation. Ne jamais faire confiance à l'utilisateur ! L'utilisation des boucles dans les programmes console est très importante.  
 
JeremGamer
 
 

SystemGlitch
Le bout de code donné à la fin du chapitre précédent vous le criait haut et fort, nous allons parler des structures conditionnelles dans ce chapitre! Enfin nous allons nous attaquer au code ! Préparez vos petits doigts !
Tout d'abord, commencez par créer une classe dans votre projet en faisant un clic droit sur "src" et en choisissant "New -> Class" :
 
 
 
Une boite de dialogue s'ouvrira, comme quand vous souhaitiez créer un projet. Nommez votre classe "Main" et cliquez sur "Finish" :
 
 
L'instant Convention :
 
Il est de retour !
Cette fois, cela concerne le nommage de vos classes.
La première lettre doit être une majuscule. Son nom ne doit pas être intégralement en majuscules. Il est mélangé de majuscules et minuscules, sans caractères spéciaux ni espaces. Comme pour les variables, mettez une majuscule au début de chaque mot composant le nom. class NomDeLaClasse  
Évitez les acronymes et donnez des noms simples et descriptifs.  
Voila ! Votre nouvelle classe toute neuve devrait ressembler à ça :
public class Main { }  
Nous allons la fournir un peu et faire en sorte que votre programme puisse se démarrer !
Nous allons donc ajouter la méthode principale. Les instructions qu'elle contient seront exécutées lors de chaque démarrage du programme.
Avant cela, je voudrais insister sur un point très important: les accolades.
Sachez que les accolades englobent et forment des sortes de paquets, ici, tout ce qui se trouvera entre les accolades déjà présentes fera partie de la classe "Main". Rien à part les imports ne peuvent se trouver à l'extérieur !
Pour se repérer, voir à quelle accolade ouvrante correspond quelle accolade fermante et inversement, on utilise quelque chose qui s'appelle l'indent. C'est simplement le fait d'aligner les portions de codes se trouvant entre plusieurs accolades. Un exemple vaut bien plus que de longues phrases :
public class Main { int i = 0; public static void main(String args[]) { System.out.println("Salut les bukkitiens!"); } }  
Vous constatez que plus on "s'enfonce" profondément dans les accolades, plus la ligne est décalée. Eclipse s'en occupe en général, mais sinon faites-le vous même à l'aide de la touche tabulation !
L'indent n'est pas obligatoire mais c'est juste un moyen pour les développeurs de se retrouver dans leur propre code, sinon ce serait un véritable enfer !
L'argument utilisé ici entre les parenthèses est de type String. Il se syntaxe toujours avec des guillemets. C'est une chaîne de caractère extrêmement utilisée mais elle n'est pas un type primitif! Nous entrerons dans les détails de ce type plutôt spécial plus tard.
Maintenant nous pouvons nous intéresser au contenu de ce code. Recopiez-le dans votre IDE. Vous pouvez retirer la ligne "int i = 0;" car elle ne nous servira pas. Elle était là juste à titre d'exemple pour l'indent.
Vous avez sûrement repéré deux nouveaux mots-clefs et d'autres choses bizarres. Ne vous en souciez pas pour l'instant. La méthode principale, c'est ceci !
Ici, le programme sait juste écrire "Salut les bukkitiens" dans la console. Désormais, vous savez aussi le faire !
Testons notre programme grâce à notre IDE. Cliquez sur la flèche verte dans votre barre d'outils. Eclipse vous demandera de sauvegarder avant d'exécuter, cliquez sur OK.
 
 
 
Vous pouvez constater que la phrase voulue s'est inscrite dans la console! Votre programme est fonctionnel! Sachez que la ligne "System.out.println()" permet d'imprimer n'importe quel type dans la console ! Aussi bien des variables numériques que des chaines de caractères ou bien plus encore !
Nous allons pouvoir enfin rentrer dans le vif du sujet. Nous allons corser un peu la chose.
Commençons par la structure conditionnelle la plus simple: if/else.
Celle-ci permet tout simplement d'exécuter des instructions en fonction du résultat d'un test. Il est possible d'y ajouter "else" à la fin. C'est littéralement "Si quelque chose, faire ceci, sinon, faire ceci." ! Reprenez le code fourni à la fin du chapitre précédent et vous comprendrez tout de suite.
int nombre1 = 6; int nombre2 = 2; boolean isMultiple; if(nombre1 % nombre2 == 0) { isMultiple = true; } else { isMultiple = false; }  
Observons pas-à-pas ce qu'il se passe.
On déclare deux variables de type int en leur assignant directement une valeur. On déclare une variable boolean qui nous permettra de savoir si les deux nombres sont multiples ou non mais on ne lui assigne pas de valeur. On ajoute un if qui teste si le modulo de nombre1 par nombre2 est égal à 0. Accolade ouvrante. Tout ce qui se trouvera ici s'exécutera si nombre1 % nombre2 = 0. Accolade fermante, else puis accolade ouvrante. Tout ce qui se trouvera ici s'exécutera si nombre1 % nombre2 != 0. (N'est pas égal à 0).  
Je vous propose de modifier un peu ce code pour qu'il puisse communiquer avec l'utilisateur.
int nombre1 = 6; int nombre2 = 2; //Nous n'avons plus besoin de la variable boolean if(nombre1 % nombre2 == 0) { System.out.println(nombre1 + " est un multiple de " + nombre2 + "."); } else { System.out.println(nombre1 + " n'est pas un multiple de " + nombre2 + "."); }  
Vous suivez toujours? Rien de compliqué n'est-ce pas? Juste une chose, pensez à mettre des "+" pour les phrases comme je l'ai fait! C'est ce que l'on appelle la concaténation.
Maintenant, exécutez votre programme de la même manière que je l'ai expliqué plus haut. Vous allez voir que votre programme vous répond bien que 6 est multiple de 2! Désormais, essayer de modifier ces nombres et plus précisément, choisissez des nombres non multiples. Relancez votre programme et constatez que la deuxième option s'est déclenchée! C'est pas beau tout ça?
Il est possible de mettre plus encore de conditions. Repensez au chapitre sur les opérateurs. && et || peuvent être utilisés ici.
Exemple :
if(nombre1 != nombre2 && nombre1 != nombre3)  
Une autre solution ajoutant de nouvelles possibilités ainsi que de l'optimisation existe: else if
Au lieu d'écrire une autre structure, on fait comme suit :
if(...) { //Instructions } else if (...) { //Instructions } else { //Instructions }  
Vous pouvez mettre autant de else if que vous le souhaitez.
Ce cas de figure fonctionne ainsi:
Si quelque chose, alors on fait cela (if) Sinon si quelque chose d'autre, on fait cela (else if) Sinon, en dernier recourt, on fait cela. (else)  
Essayez donc cette forme dans votre logiciel, toujours dans la méthode principale !
int nombre1 = 0; if(nombre1 == 1) { System.out.println("Ce nombre est égal à 1"); } else if (nombre1 == 2) { System.out.println("Ce nombre est égal à 2"); } else { System.out.println("Ce nombre n'est ni égal à 1, ni égal à 2"); }  
Lancez-le et expérimentez en changeant la valeur de nombre1 !
Il est aussi possible d'écrire les if "sur une seule ligne". Cependant, il faut bien faire attention car si plusieurs action sont à effectuer lorsqu'une condition est remplie, il faudra utiliser les accolades !
if(nombre1 == 0) System.out.println("Ce nombre est égal à 0"); else if(nombre1 == 1) System.out.println("Ce nombre est égal à 1"); else System.out.println("Ce nombre n'est ni égal à 0, ni égal à 1"); System.out.println("Le nombre a été testé!"); //Cette ligne ne fera pas partie du else! Ici l'indent vous le fait savoir sans même regarder qu'il n'y a pas d'accolades au if!  
L'optimisation qu'apporte else if est la suivante : Quand une des conditions est remplie, le reste des tests n'est pas lu. Du coup, votre programme est accéléré car il peut directement passer à la suite !
Avouez qu'il serait barbant et très long de mettre plein de if partout quand il y a un grand nombre de deux possibilités. Pour cela, il existe une structure conditionnelle très pratique qui s'appelle switch ! Sa syntaxe est assez spéciale alors lisez bien !
switch(nombre1) { case 0: //Instructions break; //On passe le reste du switch jusqu'a l'accolade fermante. case 1: //Instructions break; case 2: //Instructions break; default: //Instructions break; }  
Je vous avait prévenu! Voyons ça en détails.
Tout d'abord, on écrit le mot-clef switch puis entre parenthèses, on met la variable à tester. ATTENTION, cette structure ne fonctionne qu'avec des types primitifs, ainsi que les String depuis Java 7.
Le switch va alors vérifier la valeur de "nombre1". Dans le cas où "nombre1" est égal à la valeur spécifiée après le mot-clef case, on exécute les instructions.
Vous pouvez mettre autant de case vous voulez !
default est différent. Premièrement, pas besoin de spécifier de valeur cette fois-ci car les actions spécifiées s'exécutent si la variable a une valeur différente de tous les case au dessus ! Deuxièmement, il n'est pas obligatoire. Dans le cas où vous ne l'avez pas ajouté, rien ne se passe si aucun case ne correspond à la valeur de la variable choisie.
Prenons un exemple concret qui parlera à tout le monde: manger.
Nous allons vérifier à quel moment de la journée l'utilisateur mange et nous allons définir le comportement du programme en fonction de cela !
String moment = "Nuit"; switch(moment) { case "Matin": System.out.println("Le repas le plus important de la journée! C'est bien!"); break; case "Midi": System.out.println("La pause midi est toujours la bienvenue!"); break; case "Soir": System.out.println("Ça fait du bien de manger après une dure journée de travail!"); break; default: System.out.println("Ce n'est pas bien de grignoter entre les repas!"); break; }  
Les trois grands repas de la journée sont précisés dans les case. Mais si on mange à un autre moment, comme ici, la nuit, le programme nous répondra que c'est mal de grignoter en dehors des repas! Essayez donc ce bout de code dans votre programme. Pensez à vider le contenu de la méthode principale avant.
Un dernier aspect du switch qu'il faut connaître, c'est que l'on peu effectuer une action pour différentes possibilités, de la même manière qu'un if avec plein de "||" :
switch(mois){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: jours = 31; break; case 4: case 6: case 9: case 11: jours = 30; break; case 2: jours=28; break; }  
Ici, on vérifie de quel mois il s'agit, le nombre étant ceux que vous connaissez soit 1 pour Janvier, 2 pour Février, etc...
Pensez à faire ceci plutôt que de recopier plein de fois la même instruction !
La dernière structure est très peu utilisée mais peu s'avérer utile dans certains cas. Elle porte le nom barbare de condition ternaire.
Le principe est simple en soi, mais la pratique l'est moins si on a pas bien saisi son fonctionnement. Ne vous découragez pas en voyant la tête qu'elle a, vous comprendrez bientôt.
int i = 5; int j = 10; int max = (i < j) ? j : i;  
En réalité, nous faisons un test avant d'assigner une nouvelle valeur à max.
Je m'explique.
Entre les parenthèses, on a le test, exactement comme dans un if !
C'est là qu'intervient un nouvel opérateur! "? :" porte le nom d'opérateur ternaire de condition. Il est spécifique à cette structure, vous ne le verrez nul par ailleurs.
Bref, il s'occupe de faire le test puis de choisir entre les deux valeurs données à côté du ":".
Si le test renvoie true, donc ici que i est bien inférieur à j, alors la variable qui se trouve juste après le "?" sera assignée à max. Sinon ce sera la variable après les ":" qui le sera.
Finalement, faire ceci revient au même mais est beaucoup moins rapide :
int i = 5; int j = 10; int max; if(i < j) max = j; else max = i;  
Résumé :
 
Vous savez désormais créer un programme fonctionnel ! Les instructions contenues dans la méthode principale seront exécutées lors du démarrage du programme. La commande "System.out.println()" permet d'écrire quelque chose dans la console. L'indent correspond au décalage des lignes et permet de se repérer au sein de son code. Il existe trois types de structures conditionnelles: if(...){...}else if(...){...}else{...} switch(...) {case argument:...} (...) ? ... : ... --> La condition ternaire. Faites bien attention aux accolades. C'est très important de bien les placer! Les tests se font à l'aide de plusieurs variables ainsi que des opérateurs logiques. Faire son choix parmi ces structures conditionnelles est une question de pratique, et parfois d'optimisation.  
 
JeremGamer
 

SystemGlitch
Ce premier chapitre sera très théorique. Nous allons parler du plus basique des aspects de n'importe quel langage: les types primitifs.
 
Tous les programmes sont basés sur plusieurs, voire de nombreuses variables. Une variable est donc un objet qui peut changer de valeur. Par exemple, imaginons un programme de type calculatrice. Il nous faudra 4 variables ou plus afin d'effectuer le calcul: le premier et le deuxième nombre, définis par l'utilisateur, le résultat et pour finir, l'opérateur. (+ , - , * , /) Nous reviendrons sur cette notion plus tard car il en existe plein d'autres et ces derniers ne s'utilisent pas comme on le fait en mathématiques! Mais les variables ne sont pas forcément numériques! Cela peut très bien être une chaîne de caractère ou même des objets complètement différents comme un bouton dans une interface graphique! Mais pour l'instant, restons-en au plus simple.
 
Nous nous concentrerons sur les variables dites de type primitif.
 
-----> Mais qu'est-ce donc !?
 
Il existe 8 types primitifs. Ce sont des types de variables qui auront chacun des propriétés différentes. Ils sont désignés par des mots-clef. Vous allez donc apprendre vos premiers mots-clef par la même occasion !
byte : Valeur numérique entière comprise entre -128 et 127 (inclus). short : Valeur numérique entière comprise entre-32 768 et 32767 (inclus). int : Utilisé le plus fréquemment. Valeur numérique entière comprise entre -2 147 483 648 et 2 147 483 647 (inclus). long : Valeur numérique entière comprise entre -9 223 372 036 854 775 et 9 223 372 036 854 775 807 (inclus). Pour les distinguer, ils se terminent par un 'L'. Exemple: 584818823L float : Valeur numérique décimale comprise entre plus ou moins 10-45 et plus ou moins 1038. Pour les distinguer, ils se terminent par un 'F'. Exemple: 16.7F double : Valeur numérique décimale comprise plus ou moins 10-323 et plus ou moins 10308. char : Caractère unicode. Tiens, un intrus! Il se syntaxe de la manière suivante: 'a' ou 'b' ou 'c' etc... Ou alors en utilisant le caractère sous forme de code Unicode: '\u00e9' pour 'é'. boolean : Vous avez peut-être déjà entendu parler de valeur booléenne? C'est exactement ça! Un boolean est sois égal à true ou à false. Littéralement, vrai ou faux. Et toc! Plus deux mots-clef!  
Tous les mots-clef que vous rencontrerez dans Eclispe seront en gras et violet comme ci-dessus, à moins que vous ne vous aventuriez à changer les couleurs de votre IDE...
Je préfère tout de suite soulever un poids qui pèse sur vous: aucun besoin de retenir par cœur les valeurs minimales et maximales des types primitifs ci-dessus! Souvenez-vous en juste approximativement car s'ils sont si nombreux, ce n'est pas pour rien !
 
 
L'instant Convention
 
Avant de passer à la suite, j'introduis ces petits paragraphes qui seront présents un peu partout dans les différents chapitres. Il pourraient être renommés en "L'instant Barbant" mais ils sont importants et ne doivent pas être négligés!
Les développeurs, pour pouvoir se comprendre à travers leur code, et que les librairies soient facilement utilisables, ont mis en place de nombreuses conventions de nommage. Vous vous devez de les respecter !
 
Par convention, il est interdit de nommer une variable ou une méthode (vous verrez bientôt de quoi il s'agit) par un mot-clef. Les noms des variables et des méthodes doivent aussi commencer par une minuscule. Si ceux-ci sont composés de plusieurs mots, pas d'espaces! Mettez une majuscule sur la première lettre de chaque mot, hormis le premier. Utilisez des noms simple et descriptifs. Et pour finir, n'utilisez pas de caractères spéciaux, que des lettre de a à z, de A à Z ainsi que des nombres. (Les noms ne doivent cependant pas débuter par un nombre). Quand aux constantes, notion que nous aborderons dans quelques instants, elles doivent être nommées avec des majuscules et des "_" (underscore).
 
 
Revenons à nos variables.
 
-----> Comment choisir?
 
Procédons par ordre. Certains sont évidents. Il serait étrange de déclarer une variable de type int alors que sa valeur est censée être un caractère! Pour cela, le choix se fait sans réfléchir: un char.
 
Si vous êtes sûr et certain que votre variable n'aura que deux valeurs possibles, optez pour un boolean.
 
Si vous cherchez à représenter une valeur décimale, donc assez précise, deux choix seulement s'offrent à vous: un float ou un double? Vous choisirez en général le double car c'est le plus utilisé. Cette variable pourra donc être plus facilement utilisée. Les float seront utilisés dans certains cas, mais vous les rencontrerez plus rarement.
 
Pour les entier, il y a plus de choix. Les int seront privilégiés pour les valeurs inconnues car ils offrent une intervalle de valeurs plutôt grande sans utiliser trop de mémoire. C'est le type le plus utilisé.
Les short sont un bon compromis entre optimisation et largeur d'intervalle. Ils pèsent moins que les int mais ont une intervalle de valeurs possibles plus modérée.
Pour finir, les byte seront utilisés dans les cas où l'économie de mémoire est de mise. Par exemple lors de l'utilisation de flux pour copier un fichier.
 
Vous développerez grâce à de nombreux "bouts de code" prêts à l'utilisation et gentiment fournis par les créateurs de Java. Ces "bouts de code" font partie de ce que l'on appelle le JDK (Java Development Kit), autrement dit, l'API Java.
Vous serez alors contraints d'utiliser les mêmes types que ceux qu'utilisent ces "bouts de codes". C'est souvent comme ça que seront définis les ceux que vous utiliserez.
 
 
Maintenant, je vais vous parler d'une variante. Les constantes. Ce sont des variables ayant une valeur constante, qui ne peut pas changer. On pourrait dire qu'elle ne peuvent être que "lues". Elles sont désignées par le mot-clef final lors de leur déclaration.
 
 
J'introduis une nouvelle fois un concept de ce cours. A la fin de chaque chapitre, nous vous fournirons du code brut et sans commentaire en lien avec ce que nous avons dis précédemment. Vous serez en mesure de le comprendre si vous avez bien suivi. Il pourraient peut-être vous être utile alors gardez-les précieusement!
 
Celui qui vous sera fourni pour ce chapitre théorique sera donc un aperçu du code en Java et en même temps, il permettra d'introduire le prochain chapitre. Je vous laisse mijoter là-dessus :
final byte MIN_BYTE = -128; final byte MAX_BYTE = 127; byte b = 42; final short MIN_SHORT = -32768; final short MAX_SHORT = 32767; short s = 15680; final int MIN_INT = -2147483648; final int MAX_INT = 2147483647; int i = 48198401; final long MIN_LONG = -9223372036854775808L; final long MAX_LONG = 9223372036854775807L; long l = 490000857471L; final float MIN_FLOAT = Float.MIN_VALUE; final float MAX_FLOAT = Float.MAX_VALUE; float f = 14.2F; final double MIN_DOUBLE = Double.MIN_VALUE; final double MAX_DOUBLE = Double.MAX_VALUE; double d = 5.8498118998; char c = 'c'; boolean vrai = true; boolean faux = false;  
Essayez de modifier les valeurs des variables déclarées final. Ajoutez 1 aux variables "max" et retirez 1 aux variables "min". Vous constaterez que Eclipse s'empresse de vous annoncer une erreur. En effet, ce sont bien les limites de chaque type qui sont présentes ici!
Votre première erreur! C'est un grand pas! Car dans le domaine du développement, on apprend beaucoup de ses erreurs et c'est souvent à tâtons que l'on arrive à ses fins!
 
 
Résumé :
Des variables sont des objets ayant une valeur associée pouvant changer. Ces variables peuvent être de plein de types différents. Nous avons vu les types primitifs, il y en a huit: byte, short, int, long, float, double, char et boolean. On les choisi en fonction de leur propriétés et des possibilités qu'ils offrent. Les constantes sont des variables ne pouvant changer de valeur. Elles sont déclarées grâce au mot-clef final.  
 
JeremGamer
 

SystemGlitch
Vous les connaissez, il s'agit de ces petites fenêtres servant à afficher une information comme un avertissement ou une erreur, à demander une validation du style "Voulez-vous vraiment quitter? -> Oui / Non" ou encore demander une information à l'utilisateur.
En Java, elles portent le nom de JDialog tout simplement! Le JDK vous en propose déjà plusieurs toutes prêtes, ce qui est très pratique, mais si jamais vous avez besoin d'une boite de dialogue plus spécifique, il faudra alors la faire vous-même! N'ayez crainte, rien de bien difficile, c'est la même chose que pour les JFrame à une ou deux différences près.
 
Première différence: le reste du programme est figé tant que la boite de dialogue n'est pas fermée, à moins d'utiliser un Thread à part, mais nous n'avons pas encore vu comment faire.
Seconde différence: il est possible de rendre les JDialog modales, c'est à dire que tant qu'elles seront ouvertes, on ne pourra rien faire d'autre sur le logiciel que d’interagir avec la boite de dialogue. Vous en avez aussi rencontré j'en suis sûr.
 
Commençons par la partie la plus simple : les boites de dialogue informatives. Attention, pour les dialogs préfabriquées on utilisera JOptionPane et non pas JDialog ! Nous allons ajouter trois boutons à notre content pane, chacun ouvrira une boite de dialogue différente. Ces dernières sont modales, ce qui vous permettra de vous faire d'ores et déjà un idée de ce que cela signifie.
  JButton info = new JButton("Ouvrir boite d'information"); info.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { JOptionPane.showMessageDialog(null, "Message d'information", "Information", JOptionPane.INFORMATION_MESSAGE); //message d'information } }); JButton warn = new JButton("Ouvrir boite d'avertissement"); warn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { JOptionPane.showMessageDialog(null, "Message préventif", "Attention", JOptionPane.WARNING_MESSAGE); //Message d'avertissement } }); JButton err = new JButton("Ouvrir boite d'information"); err.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { JOptionPane.showMessageDialog(null, "Message d'erreur", "Erreur", JOptionPane.ERROR_MESSAGE); //Message d'erreur } });  

 
Revenons plus en détails sur la méthode que j'ai utilisé :
JOptionPane.showMessageDialog(null, "Message d'information", "Information", JOptionPane.INFORMATION_MESSAGE);  
Le premier argument est le JComponent parent. C'est à dire le composant au dessus du quel devra s'ouvrir la fenêtre. S'il y en a pas comme ici, on met null. On aurait cependant pu mettre this pour centrer la boite de dialogue au dessus du content pane, même si la fenêtre principale avait été déplacée.
Le second argument est le message que l'utilisateur verra dans la boite de dialogue.
Le troisième est le titre de la boite de dialogue, affiché dans la barre d'action.
Et le dernier est celui qui définira s'il s'agit d'un message d'information, d'erreur ou d'avertissement. D'ailleurs il est possible de simplement mettre un message, sans icône, à l'aide de JOptionPane.PLAIN_MESSAGE, ou alors une question avec JOptionPane.QUESTION_MESSAGE.
 
Cette méthode est surchargée. Voyons un peu ce que l'on peut faire de plus ! Nous avons deux surcharges à notre disposition, l'une rajoute un argument de type ImageIcon, qui nous permettra donc de changer l'icône, l'autre permet de créer un boite de dialogue la plus simple qui soit car seuls les deux premiers arguments sont conservés.
 
Voici comment changer l'icône qui sera affichée :
ImageIcon img = new ImageIcon("check.png"); JOptionPane.showMessageDialog(null, "Sauvegarde réussie!", "Succès", JOptionPane.INFORMATION_MESSAGE , img);  

 
Jetons maintenant un œil aux boites de confirmation. Cette fois la méthode utilisée sera showConfirmDialog(). La différence c'est que celle-ci retourne un int indiquant le choix de l'utilisateur. Vous verrez ce sera tout de suite plus clair en voyant le code :
int option =JOptionPane.showConfirmDialog(null, "Êtes-vous sûr de vouloir quitter?", "Vraiment?" , JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); //Vous pouvez encore changer l'icône en ajoutant une argument du type ImageIcon if(option == JOptionPane.YES_OPTION) System.out.println("Quitter"); else if(option == JOptionPane.NO_OPTION) System.out.println("Ne pas quitter");  

    int option =JOptionPane.showConfirmDialog(null, "Le fichier est introuvable, continuer?", "Fichier introuvable" , JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); if(option == JOptionPane.YES_OPTION) System.out.println("Continuer"); else if(option == JOptionPane.NO_OPTION) System.out.println("Ne pas continuer"); else if(option == JOptionPane.CANCEL_OPTION) System.out.println("Annuler"); else if(option == JOptionPane.CLOSED_OPTION) //L'utilisateur a fermé la boite de dialogue sans passer par les boutons "Oui" "Non" ou "Annuler" System.out.println("Fermer");  

 
Le principe est acquis, on passe à la suite! En effet ici on stockait une int renvoyée par la méthode, mais si on faisait une boite de dialogue qui demande à l'utilisateur d'écrire une chaine de caractères? Et bien c'est pareil! Sauf qu'on aura une String en retour :
String answer = JOptionPane.showInputDialog(null, "Comment vous appelez-vous?", "Vos identifiants" , JOptionPane.QUESTION_MESSAGE); System.out.println("L'utilisateur s'appelle \""+ answer + "\".");  

 
 
Allons un peu plus loin et proposons différents choix via un menu déroulant. On créera un tableau contenant les valeurs possibles et le donnera en argument :
String[] sex = { "Homme" , "Femme" , "Autre" }; String answer = (String) JOptionPane.showInputDialog(null, "Veuillez indiquer votre sexe.", "Vos identifiants" , JOptionPane.QUESTION_MESSAGE , null , sex , sex[0]); System.out.println("Sexe de l'utilisateur: " + answer);  

 
Le dernier argument est celui qui sera sélectionné par défaut dans le menu déroulant. Attention si l'utilisateur ferme la boite de dialogue autrement qu'en appuyant sur "OK", la valeur retournée sera null, et cela est aussi valable pour le champ de saisie vu juste au dessus! Il faudra donc bien vérifier vos résultats! Quand à l'argument null donné dans la méthode, il s'agit de l'icône modifiée. On ne souhaite pas la changer donc on donne null.
 
Voici la dernière variante de boite de dialogue préfabriquée: c'est le même principe que celle que nous venons de voir mais cette fois il y aura plusieurs boutons et non pas un menu déroulant.
String[] sex = { "Homme" , "Femme" , "Autre" }; int answer = JOptionPane.showOptionDialog(null, "Veuillez indiquer votre sexe.", "Vos identifiants" , JOptionPane.YES_NO_CANCEL_OPTION , JOptionPane.QUESTION_MESSAGE, null , sex , sex[0]); //answer est le numéro du bouton qui a été cliqué par l'utilisateur, -1 si la fenêtre à été fermée par un autre moyen //Vous pouvez donc mettre plus ou moins de trois boutons if(answer >= 0) System.out.println("Sexe de l'utilisateur: " + sex[answer]);  

 
Bien, vous avez déjà de quoi faire. Mais certains cas ne sont pas gérés, ou alors, vous souhaitez simplement faire un boite de dialogue ayant un style visuel bien particulier. Vous savez déjà faire tout ça! Le procédé est exactement le même que pour la création d'une JFrame, sauf que votre classe héritera de JDialog.
Votre code cadeau sera une boite de dialogue d'erreur faite maison! Pensez à l'utiliser avec le code cadeau donné dans le chapitre sur les exceptions !
package fr.bukkit.jeremgamer.cours; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.PrintWriter; import java.io.StringWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.swing.Box; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.UIManager; public class CustomDialog extends JDialog { private JLabel message = new JLabel("<html><b>Une erreur est survenue.</b><br>Certaines fonctionnalités risquent des disfonctions.<br>Il est conseillé de redémarrer le logiciel.<br><em>Support: [email protected]<script cf-hash='f9e31' type="text/javascript"> /* */</script></em></html>"); private JButton copy = new JButton("Copier le rapport"); private JButton close = new JButton("Ok"); private Toolkit toolkit = Toolkit.getDefaultToolkit(); private Clipboard cb = toolkit.getSystemClipboard(); private DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); private Date date; private StringWriter sw = new StringWriter(); private PrintWriter pw = new PrintWriter(sw); private Throwable e; public CustomDialog(final Throwable e) { date = new Date(); this.e = e; this.setModal(true); //On rend notre boite de dialogue modale this.setTitle("Erreur"); this.setResizable(false); this.setSize(new Dimension(400, 150)); this.setLocationRelativeTo(null); JPanel messageContent = new JPanel() { /** * */ private static final long serialVersionUID = 3692136522118161020L; protected void paintComponent(final Graphics g) { Icon icon = UIManager.getIcon("OptionPane.errorIcon"); BufferedImage image = new BufferedImage( icon.getIconWidth() , icon.getIconHeight() , BufferedImage.TRANSLUCENT ); icon.paintIcon(null, image.getGraphics() , 0 , 0 ); g.drawImage(image, 20 , 35, 48 , 48 , null); super.paintComponents(g); } }; messageContent.add(Box.createRigidArea(new Dimension(400 , 0))); messageContent.add(Box.createRigidArea(new Dimension(70 , 0))); FlowLayout flow = new FlowLayout(); flow.setAlignment(FlowLayout.RIGHT); flow.setVgap(5); flow.setHgap(5); JPanel buttons = new JPanel(flow); buttons.add(copy); buttons.add(close); final Container glass = (Container) this.getGlassPane(); glass.setVisible(true); glass.setLayout(new BorderLayout()); glass.add(buttons , BorderLayout.SOUTH); messageContent.add(message); close.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { dispose(); } }); copy.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { cb.setContents( new StringSelection(getReport()) , null ); } }); this.setContentPane(messageContent); this.setVisible(true); } private String getReport() { e.printStackTrace(pw); return new String( "Rapport d'erreur :\n" + "Version : " + "0.0.1" + "\n" + "Date : " + dateFormat.format(date) + "\n" + "Système d'exploitation : " + System.getProperty("os.name") + " " + System.getProperty("os.version") +"\n\n" + "StackTrace :\n" + sw.toString()); } }  

 
 
Résumé :
 
Les boites de dialogue sont de petites fenêtres permettant d'afficher une information ou d'en demander une à l'utilisateur. Elles figent le reste du programme tant qu'elles sont ouvertes. Les boites de dialogues modales empêchent toute interaction avec le reste du logiciel tant qu'elle sont ouvertes. Pour les boites de dialogue préfabriquées, on utilisera JOptionPane. Pour les boites de dialogue personnalisées, on créera une classe qui hérite de JDialog.  
 
JeremGamer
 

SystemGlitch
Dans ce chapitre, vous apprendrez à créer vos propres menus! Nous verrons les barres de menu que vous avez l'habitude de voir en haut des fenêtres, là où se trouve "Fichier", "Édition", etc... ainsi que les menus pop-up, aussi appelés menus contextuels, pouvant apparaître n'importe où! Vous avez l’habitude de voir ces derniers apparaitre lorsque vous cliquez-droit.
 
Comme tout ce que nous voyons depuis un moment, il ne s'agit que de l'utilisation de composants Swing, ce qui veut dire que ce sera très simple une fois de plus.
 
Voici le principe: une barre de menu JMenuBar, des menus JMenu et des items à mettre dedans, les JMenuItem. Attention cependant : cette fois-ci nous travaillons sur l'objet de notre fenêtre et non sur le content pane. Il existe des variantes de JMenuItem qui permettent d'insérer les autres composants de formulaire que vous avez vu jusque là: le JCheckBoxMenuItem et JRadioButtonMenuItem.
 
En pratique, cela donne quelque chose qui ressemble à ça :
  JMenuBar menuBar = new JMenuBar(); JMenu file = new JMenu("Fichier"); JMenuItem new_ = new JMenuItem("Nouveau"); JMenuItem open = new JMenuItem("Ouvrir"); JMenuItem save = new JMenuItem("Sauvegarder"); JMenu subMenu = new JMenu("Sous-menu"); JRadioButtonMenuItem rb0 = new JRadioButtonMenuItem("Choix n°1"); JRadioButtonMenuItem rb1 = new JRadioButtonMenuItem("Choix n°2"); ButtonGroup bg = new ButtonGroup(); bg.add(rb0); bg.add(rb1); subMenu.add(rb0); subMenu.add(rb1); file.add(new_); file.add(open); file.addSeparator(); //On ajoute une petite barre de séparation file.add(subMenu); file.addSeparator(); file.add(save); JMenu edit = new JMenu("Édition"); JCheckBoxMenuItem cb0 = new JCheckBoxMenuItem("Option n°1"); JCheckBoxMenuItem cb1 = new JCheckBoxMenuItem("Option n°2"); edit.add(cb0); edit.add(cb1); menuBar.add(file); menuBar.add(edit); this.setJMenuBar(menuBar); //On applique la barre à la fenêtre  

 
Je vous laisse expérimenter par vous-même. Vous aurez surement deviné la manière d’interagir avec nos items. Car pour l'instant, ils ne font rien, absolument rien. Comme d'habitude, nous utiliserons les Listener :
JMenuItem new_ = new JMenuItem("Nouveau"); new_.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Menu \"" + new_.getText() + "\" cliqué!"); } });  
Allons plus loin : ajoutons des raccourcis clavier. Il en existe deux types: les accélérateurs, qui permettent un simple raccourci vers un élément du menu, et les mnémoniques qui permettent de simuler un clic sur un point de menu, ce qui revient presque au même. Ces derniers seront utilisés pour les menus et non pour les items.
JMenu file = new JMenu("Fichier"); JMenuItem new_ = new JMenuItem("Nouveau"); new_.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Item \"" + new_.getText() + "\" cliqué!"); } }); new_.setAccelerator(KeyStroke.getKeyStroke('n')); //Touche n JMenuItem open = new JMenuItem("Ouvrir"); open.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Item \"" + open.getText() + "\" cliqué!"); } }); open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O , KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK)); //CTRL+SHIFT+O JMenuItem save = new JMenuItem("Sauvegarder"); save.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Item \"" + save.getText() + "\" cliqué!"); } }); save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S , KeyEvent.CTRL_MASK)); //CTRL+S  

 
Vous pouvez voir que les raccourcis s'affichent à côté de l'item en plus de ça !
 
Pour les mnémoniques, c'est le même principe, sauf qu'il faudra presser Alt + la touche définie pour que le menu se déroule :
JMenu file = new JMenu("Fichier"); file.setMnemonic('F'); //... JMenu edit = new JMenu("Édition"); edit.setMnemonic('É');  

 
Cette fois le caractère défini est souligné s'il est présent dans le texte du menu.
 
Vous pouvez aussi initialiser les JMenu avec un mnémonique directement :
JMenuItem open = new JMenuItem("Ouvrir" , 'O');  
J'ai une bonne nouvelle : ça ne s'arrête pas là ! Il est possible, en réalité, d'ajouter n'importe quel JComponent au menu !
file.add(new JButton("Bouton")); file.add(new JLabel("Label")); file.addSeparator(); JPanel pan = new JPanel(); pan.setBackground(Color.RED); file.add(pan);  

 
Et mieux encore : il est même possible de leur attribuer un mnémonique ! (Mais pas d'accélérateur) Ce dernier fonctionnera uniquement si le menu est déjà déroulé. Il y a quand cependant quelques exceptions : les JLabel ou les JPanel par exemple, car ils ne sont pas censé être des composants avec lesquels l'utilisateur interagit.
 
 
Passons aux menus contextuels. Vous savez l'essentiel, plus qu'à rajouter quelques fonctionnalités comme la position où apparaitra le menu ainsi que le fait qu'il apparaisse à l'aide d'un clic droit. Commençons par préparer notre menu :
JPopupMenu menu = new JPopupMenu(); JMenuItem new_ = new JMenuItem("Nouveau"); JMenu subMenu = new JMenu("Sous-menu"); JRadioButtonMenuItem rb0 = new JRadioButtonMenuItem("Choix n°1"); JRadioButtonMenuItem rb1 = new JRadioButtonMenuItem("Choix n°2"); ButtonGroup bg = new ButtonGroup(); bg.add(rb0); bg.add(rb1); subMenu.add(rb0); subMenu.add(rb1); menu.add(new_); menu.add(open); menu.addSeparator(); menu.add(subMenu); menu.addSeparator(); menu.add(save);  
Parfait. Maintenant plus qu'à faire en sorte qu'il apparaisse avec le clic droit :
JPanel content = new JPanel(); content.addMouseListener(new MouseAdapter(){ public void mouseReleased(MouseEvent event){ if(event.isPopupTrigger()) //Clic droit menu.show(content, event.getX(), event.getY()); //Afficher le menu sur le contentPane } }); this.setContentPane(content);  

 
Pour vous montrer une autre application possible des JPopupMenu, voici un petit code cadeau que je vous invite fortement à tester. Ce n'est pas le meilleur moyen de faire ce qu'il fait, pas non plus le plus optimisé, mais ça illustre bien ce que pourrait faire cet objet.
ArrayList<String> producList = new ArrayList<String>(); productList.add("Aspirateur"); productList.add("Moteur"); productList.add("Tondeuse"); productList.add("Machine à laver"); productList.add("Fer à repasser"); productList.add("Mixeur"); productList.add("Micro-ondes"); productList.add("Four"); productList.add("Plaque de cuisson"); //... JPopupMenu suggestions = new JPopupMenu(); suggestions.setMinimumSize(new Dimension(200 , 25)); suggestions.setFocusable(false); JTextField searchField = new JTextField(); searchField.setPreferredSize(new Dimension(200 , 25)); searchField.addCaretListener(new CaretListener() { @Override public void caretUpdate(CaretEvent e) { suggestions.removeAll(); for(String s : productList) if(s.startsWith(searchField.getText())) suggestions.add(new JMenuItem(s)); if(suggestions.getComponents().length > 0) { suggestions.show(searchField, searchField.getX(), searchField.getY() + searchField.getHeight() - 7); suggestions.pack(); suggestions.revalidate(); suggestions.repaint(); } } });  
 
Résumé :
 
Les JMenu sont placés dans une JMenuBar ou dans un JPopupMenu. Ils contiennent des JMenuItem. Tous les JComponent peuvent être ajoutés aux JMenu.  
 
JeremGamer
 

SystemGlitch
Maintenant que vous avez un certain nombre de compétences en matière d'interfaces graphiques, vous serez en mesure de créer des logiciels bien plus "user-friendly" que de vulgaires consoles. Vous savez déjà plus ou moins ce qui vous attend dans ce TP, alors sans plus tarder, voici votre cahier des charges ! Lisez-le attentivement et surtout, dans son intégralité !
 
Nous voulons réaliser une petite interface graphique permettant de personnaliser un personnage de jeu de rôle. De nombreux champs de formulaires devront être utilisés afin de pouvoir paramétrer les caractéristiques suivantes :
Nom du personnage Sexe du personnage Tranche d'âge du personnage Taille du personnage Couleur de peau Couleur de cheveux Couleur des yeux
Vous pouvez ajouter des caractéristiques si vous le souhaitez.
En option, vous pouvez rajouter une image sur le côté, pour représenter le personnage, et mieux encore, vous pouvez la faire réagir avec les caractéristiques telles que le sexe ou la couleur de peau.
Ensuite nous souhaitons également que l'interface soit la plus claire possible, tout en étant bien structurée et organisée. Pour cela il vous faudra combiner de nombreux panneaux ayant des layout différents.
 
Histoire de ne pas perdre son travail, nous souhaitons également que toutes les caractéristiques soient sauvegardées et rechargées au lancement du programme. Le fichier de sauvegarde sera localisé dans le même dossier que le logiciel, histoire de s'y retrouver pendant les tests. On pourra éventuellement changer ça plus tard. Je dois vous obliger à une chose cependant: je souhaite que vous sauvegardiez toutes ces données à l'aide d'un ObjectOutputStream ! Les données devront être rechargées dans tous les champs de formulaire au lancement du logiciel si un fichier existe.
 
Lors de la fermeture de la fenêtre, les données doivent être sauvegardées. Voici une petite astuce pour effectuer quelques instructions lors de différents événements liés à une fenêtre :
this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { //Effectué juste avant que la fenêtre se ferme et que le processus se termine } @Override public void windowDeiconified(WindowEvent e) { //Lorsque la fenêtre est restaurée après réduction } @Override public void windowIconified(WindowEvent e) { //Lorsque la fenêtre est réduite } });
Comme d'habitude la documentation peut vous servir pour savoir quelles autres méthode proposent les WindowListener.
 

Vous savez tout ! Maintenant fini de rêvasser, au travail ! J'apprécierais voir vos travaux une fois terminés alors n'hésitez pas à me les envoyer par message privé Si vous avez besoin d'aide, je suis là également pour vous aiguiller !
N'oubliez pas ce que je vous ai dis pendant les deux autres TPs : prenez votre temps, réfléchissez bien avant de vous jeter la tête la première dans le code, etc... Cela s'applique pour tous vos projets ! Il y a cependant une petite différence : vous travaillez maintenant sur GUI, alors pensez bien à la forme qu'elle prendra. N'hésitez pas à vous faire un petit dessin si ça peut vous aider à bien visualiser. De plus, choisissez judicieusement les composants que vous allez utiliser; ça n'aurait aucun sens d'utiliser un champ de saisie pour choisir le sexe du personnage.
 
Je vous souhaite bonne chance !
 
Correction :
 
 
JeremGamer
 

SystemGlitch
Comme promis, nous allons revenir en détails sur les Listener, comment fonctionnent-t-ils? Comment font ils pour savoir qu'ils peuvent écouter tel objet? Par quel procédé remarquent-ils que quelque chose c'est produit dans cet objet? Toutes ces questions vont trouver une réponse dans ce chapitre
 
Reprenons l'exemple du début du chapitre précédent :
 
Nous allons entrer un peu plus dans les détails, en faisant le lien avec du code. Imaginons qu'il y a deux enfants. Sous forme de code on aura quelque chose qui ressemble à ça :
//Dans la classe Parents private void appelerATable() { enfant1.venirATable(); enfant2.venirATable(); }  
Problème: imaginez que le couple décide d'avoir un nouvel enfant. Quand ils appelleront à table, seuls les deux premiers viendront.
Vous allez me dire qu'il suffit d'une petite ArrayList et un boucle for, en ajoutant le nouvel enfant à la liste. J'ai mieux à proposer ! Nous allons élever ces enfants pour qu'ils écoutent et non qu'ils soient forcés à exécuter ce que l'on leur demande. Car dans le code ci-dessus, finalement les enfants ne viennent pas tout seul, ce sont les parents qui viennent les chercher ! Cependant vous remarquerez bien vite que cette proposition n'est pas sans intérêt, au contraire ! il faudra juste y ajouter quelques subtilités.
 
Première étape: apprendre aux parents à se faire écouter par leurs enfants, peu importe le nombre qu'ils sont. Ce qui est bien, c'est que cela ne s'appliquera pas qu'aux enfants ! Je m'explique: nos parents seront donc écoutables, ce qui signifie que n'importe quel objet pourra agir en conséquences des actes de ces derniers. Si jamais vous avez à rajouter des éléments dans votre programme, comme par exemple les grands-parents ou les amis, et bien il faudra adapter votre classe Parents afin qu'elle supporte cette nouveauté. Nous allons optimiser tout ça, plus besoin de toucher au code des parents !
 
On appelle cela le pattern observer. Les patterns sont des logiques de programmation pour optimiser le code en vue de futurs changements possibles, afin de rendre chaque objet complètement indépendant et rendre votre code 100% modulable !
Ici on utilisera le patter observer car il se prête totalement à notre exemple: il s'agit "d'écouter".
 
Maintenant que le concept est clair dans votre tête, passons à la pratique ! Lisez attentivement le code qui suit.
package fr.bukkit.jeremgamer.cours; import java.util.Observable; public class Parent extends Observable { public static final String APPEL_A_TABLE = "AAAAAAAAAAAA TAAAAAAAABBLEEEEEEEUUUUUUX!!!!"; protected void appelerATable() { crier(APPEL_A_TABLE); } private void crier(String message) { setChanged(); //Héritée de Observable, indique qu'un changement est effectué //Utile lorsqu'une variable est changée et que les Observers doivent en être informés notifyObservers(message); //On notifie le message aux objets écoutant } protected Enfant faireEnfant() { Enfant enfant = new Enfant(); //L'enfant est né! //On ajoute l'enfant aux Observer du Parent addObserver(enfant); //Méthode héritée de Observable return enfant; } } package fr.bukkit.jeremgamer.cours; import java.util.Observable; import java.util.Observer; public class Enfant implements Observer { @Override public void update(Observable o, Object arg) { if(o instanceof Parent) if(arg.equals(Parent.APPEL_A_TABLE)) venirATable(); } private void venirATable() { System.out.println("Un enfant est venu à table."); } }  
Et pour finir, dans la méthode principale :
public static void main(String[] args) { Parent parent = new Parent(); parent.faireEnfant(); //On fait deux enfants parent.faireEnfant(); parent.appelerATable(); }  
Essayons :
Un enfant est venu à table. Un enfant est venu à table.  
C'est bien ce que nous voulions ! Cela fonctionne très bien ! Mis a part le fait que le parent peut faire des enfants tout seul et qu'il les appelle directement à table à peine leur naissance terminée, on peut voir que ces derniers écoutent bien ! Pour le réalisme, nous allons faire abstraction pour cette fois.
 
Revenons en détails sur ce code :
Vous pouvez constater que l'on a effectué aucun référencement! Ce qui est déjà un grand pas. Regardez à nouveau le code ci-dessus et constatez que... Nos objets Enfant et Parent sont pas encore complètement indépendants car dans la méthode faireEnfant() de la classe Parent, on instancie un Enfant. Le problème est simple à régler, je suis certain que vous avez déjà deviné la solution !
 
Rien de bien compliqué, retirons cette méthode. Nous procéderons donc ainsi, ce qui revient strictement au même :
Parent parent = new Parent(); parent.addObserver(new Enfant()); parent.addObserver(new Enfant()); parent.appelerATable();  
Nos deux classes sont désormais bien indépendantes! Tellement que l'on pourra comme je vous l'ai dis tout à l'heure ajouter un nouveau type de personnes sans aucun problème, et sans avoir à toucher au code d'une autre classe que celle du nouveau personnage !
 
C'est bien beau tout ça mais moi il y a quand même quelque chose qui me chiffonne... Vous ne savez pas de quoi il s'agit?
Si je vous rappelle que Observable est une classe, toujours aucune réaction?
 
Je pense que cette dernière information suffira donc à vous mettre en évidence le problème : rappelez-vous que Java ne gère pas l'héritage multiple !
Ah voila! Je sais que vous avez saisi désormais ! Comment faire si notre objet à écouter hérite déjà d'une autre classe? Ce qui sera d'ailleurs très souvent le cas dans la programmation de GUI... Et oui, quasiment toutes nos classes hériteront de classes issues du package swing ou AWT, comme nous l'avons déjà fait avec les JPanel ou les JButton.
 
Mais comme toujours, il existe une solution ! Car il y a toujours une solution! Nous allons réarranger tout ça à l'aide de nos propres interfaces. De plus cela vous permettra de savoir exactement comment fonctionne les Observable; c'est tout simple :
package fr.bukkit.jeremgamer.cours; public interface Observable_ { //Notez bien l'underscore, c'est une convention lorsqu'on utilise des noms déjà utilisés dans l'API Java public void addObserver(Observer_ obs); public void updateObservers(String message); public void deleteObserver(Observer_ obs); } package fr.bukkit.jeremgamer.cours; public interface Observer_ { public void update(String message); } package fr.bukkit.jeremgamer.cours; import java.util.ArrayList; public class Parent implements Observable_ { public static final String APPEL_A_TABLE = "AAAAAAAAAAAA TAAAAAAAABBLEEEEEEEUUUUUUX!!!!"; private ArrayList<Observer_> listObservers = new ArrayList<Observer_>(); protected void appelerATable() { crier(APPEL_A_TABLE); } private void crier(String message) { updateObservers(message); } protected Enfant faireEnfant() { Enfant enfant = new Enfant(); addObserver(enfant); return enfant; } @Override public void addObserver(Observer_ obs) { listObservers.add(obs); } @Override public void updateObservers(String message) { for(Observer_ obs : listObservers) obs.update(message); } @Override public void deleteObserver(Observer_ obs) { listObservers.remove(obs); } } package fr.bukkit.jeremgamer.cours; public class Enfant implements Observer_ { @Override public void update(String message) { if(message.equals(Parent.APPEL_A_TABLE)) venirATable(); } private void venirATable() { System.out.println("Un enfant est venu à table."); } }  
Si vous testez ce code, vous remarquerez qu'il fonctionne aussi bien qu'en utilisant la méthode proposée par l'API Java, mais l'héritage de l'Observable est désormais libre !
 
Il faudra bien-sûr adapter ce code à vos besoins. Ici on as juste un message donc on se contentera de cette version simplifiée, mais rien ne vous empêche d'essayer de reproduire quelque chose d'aussi pratique et adapté à toutes les situations que celui proposé par l'API Java.
 
 
Résumé :
 
Les patterns sont des logiques de programmation visant à préparer le code à de futurs ajouts et modifications, le tout avec une facilité déconcertante. Observable est une classe permettant à celles qui en héritent de pouvoir être écoutées par les classes implémentant l'interface Observer.  
 
JeremGamer
 

SystemGlitch
Dans ce chapitre nous verrons deux nouveaux layouts, le premier est assez simple à utiliser et permet de superposer plusieurs panneaux et de passer de l'un à l'autre facilement et le second est quand à lui très complexe mais permet de créer une disposition en grille avec des cases de différentes tailles.
 
Commençons sans plus tarder avec le CardLayout ! Comme expliqué ci-dessus, il permet de vous faciliter la vie lorsque vous avec besoin de passer d'une scène à l'autre de votre logiciel. Par exemple, lorsque vous avez un menu permettant d'accéder aux différentes fonctionnalités du logiciel sur la gauche, et les options de ces fonctionnalités à droite en grand. Pour passer de l'une à l'autre, vous n'allez pas retirer un panneau puis en ajouter un autre à chaque fois. Premièrement cela risque fort de perturber votre disposition générale. De plus, ce n'est absolument pas optimisé et cela consommera beaucoup de ressources de recalculer les panneaux et le layout à chaque changement. On utilisera alors le CardLayout sur un conteneur qui contiendra tous ces panneaux ! Nos panneaux seront tout simplement tous calculés à l'instanciation du conteneur, la disposition ne changera pas et on pourra changer de panneau à volonté sans aucun problème !
 
Trêve de parlotte, voici le code permettant de le faire. Je l'expliquerais ensuite. Je vous invite fortement à le recopier et le tester pour voir le résultat. Vous ne comprendrez pas tout c'est normal, car il y a des notions que vous ne connaissez pas encore à l'intérieur, notamment les actions sur les boutons.
  package fr.bukkit.jeremgamer.cours; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JPanel; public class ContentPanel extends JPanel { /** * */ private static final long serialVersionUID = -7311158212818984619L; private CardLayout cl = new CardLayout(); //On instancie le layout private final String CONTENT[] = {"red" , "green" , "blue"}; //On crée un tableau contenant les identifiants JPanel container = new JPanel(cl); //On instancie le conteneur public ContentPanel() { //On instancie les panneaux que l'on ajoutera au conteneur JPanel red = new JPanel(); red.setBackground(Color.RED); //On change leur couleur de fond pour les différencier red.setName(CONTENT[0]); //Facultatif, on l'utilisera juste pour retrouver quel panneau est acutellement affiché JPanel green = new JPanel(); green.setBackground(Color.GREEN); green.setName(CONTENT[1]); JPanel blue = new JPanel(); blue.setBackground(Color.BLUE); blue.setName(CONTENT[2]); container.add(red , CONTENT[0]); //On ajoute le panneau rouge avec l'identifiant "red" container.add(green , CONTENT[1]); //On ajoute le panneau vert avec l'identifiant "green" container.add(blue , CONTENT[2]); //On ajoute le panneau bleu avec l'identifiant "blue" //---------------------------------------- //On passe à la création du panneau de contrôle qui nous permattra de passer d'un panneau à l'autre //Ne vous attardez pas trop sur cette partie, ce qu'elle contient n'est pas encore de votre niveau JPanel control = new JPanel(); JComboBox<String> index = new JComboBox<String>(); for(String s : CONTENT) index.addItem(s); index.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @SuppressWarnings("unchecked") JComboBox<String> box = (JComboBox<String>) e.getSource(); cl.show(container, (String)box.getSelectedItem()); //On affiche le panneau portant l'identifiant sélectionné dans le menu déroulant } }); JButton previous = new JButton("Précédent"); previous.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cl.previous(container); //On affiche le panneau précédent (Ajouté avant celui acutellement affiché) //Si le panneau affiché et le premier, alors le dernier panneau sera affiché à l'appel de cette méthode index.setSelectedItem(getCurrentCard().getName()); //On actualise le menu déroulant pour qu'il affiche le bon identifiant } }); JButton next = new JButton("Suivant"); next.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cl.next(container); //On affiche le panneau suivant (Ajouté après celui acutellement affiché) //Si le panneau affiché et le dernier, alors le premier panneau sera affiché à l'appel de cette méthode index.setSelectedItem(getCurrentCard().getName()); } }); //On ajoute tous les contrôles au panneau de contrôle control.add(previous); control.add(next); control.add(index); //On ajoute le conteneur et le panneau de contrôle au panneau principal this.setLayout(new BorderLayout()); this.add(container , BorderLayout.CENTER); this.add(control , BorderLayout.SOUTH); } private JPanel getCurrentCard() { //Pour récupérer le panneau actuellement affiché JPanel card = null; for (Component comp : container.getComponents()) if (comp.isVisible() == true) card = (JPanel)comp; return card; } }  
Oui c'est un peu long, mais le plus important est en haut du constructeur, avant le trait de séparation fait avec un commentaire. Je vous avais prévenu que la longueur de vos programmes allait beaucoup augmenter !
Voici le résultat en images :
 

 
Vous avez vu que j'utilisais un tableau pour stocker les identifiants, mais vous pouvez très bien faire sans et spécifier directement les identifiants lors de l'ajout au conteneur :
container.add(panel , "id");  
Utiliser un tableau assure simplement de s'y retrouver dans les identifiants et de ne pas en oublier. C'est d'ailleurs aussi efficace pour tous les utiliser rapidement comme je l'ai fait ici :
for(String s : CONTENT) index.addItem(s);  
Vous n'avez très certainement pas compris le reste et c'est normal car cela relève de la programmation événementielle, que nous aborderons très bientôt.
 
 
Passons maintenant aux choses sérieuses ! Nous allons nous attaquer à un gros morceau en parlant du GridBagLayout !
 
Le conteneur contiendra de nombreuses cellules faisant office de cases dans un grille, chaque composant sera ajouté à l'aide de la coordonnée de la cellule voulue, et pourra occuper une ou plusieurs d'entre elles ! C'est ainsi que comme je l'ai dit au début de ce chapitre, on pourra obtenir une grille avec des cases de tailles différentes.
 
Les coordonnées se décomposent de la même manière que dans un tableau bi-dimensionnel, c'est à dire que la première case sera aux coordonnées (0 , 0) celle en dessous se trouvera à (0 , 1), la case à droite de cette dernière sera à (1 , 1).
Gardez à l'esprit que la coordonnée x est le numéro de la colonne, et y le numéro de la ligne.
 
Nous procèderons ainsi :
Définir les coordonnées Indiquer si le composant utilisera plusieurs cellules en vertical ou en horizontal. Indiquer quand une ligne est terminée  
Voici quelle forme cela va prendre :
package fr.bukkit.jeremgamer.cours; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import javax.swing.JPanel; public class ContentPanel extends JPanel { /** * */ private static final long serialVersionUID = -7311158212818984619L; public ContentPanel() { //Instanciation des différentes cellules JPanel cell0 = new JPanel(); cell0.setBackground(Color.BLACK); JPanel cell1 = new JPanel(); cell1.setBackground(Color.BLUE); JPanel cell2 = new JPanel(); cell2.setBackground(Color.CYAN); JPanel cell3 = new JPanel(); cell3.setBackground(Color.GREEN); JPanel cell4 = new JPanel(); cell4.setBackground(Color.MAGENTA); JPanel cell5 = new JPanel(); cell5.setBackground(Color.ORANGE); JPanel cell6 = new JPanel(); cell6.setBackground(Color.RED); //Conteneur JPanel container = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); //Instanciation de l'objet gérant les contraintes gbc.weightx = 1; //On demande de l'espace supplémentaire pour le redimensionnement gbc.weighty = 1; //Chaque cellule serait très petite sinon gbc.fill = GridBagConstraints.BOTH; //On remplit autant de cases que l'on peut en verticalité et en horizontalité //On positionne dans la première case. gbc.gridx = 0; gbc.gridy = 0; //On définit la taille que le composant empruntera (en cases) gbc.gridwidth = 1; gbc.gridheight = 1; container.add(cell0 , gbc); //On ajoute la cellule 0 en spécifiant la contrainte //------------------------- gbc.gridx = 1; gbc.gridheight = 2; container.add(cell1 , gbc); //------------------------- gbc.gridx = 2; gbc.gridheight = 1; container.add(cell2 , gbc); //------------------------- gbc.gridx = 3; container.add(cell3 , gbc); //------------------------- gbc.gridwidth = GridBagConstraints.REMAINDER; //Fin de ligne gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; container.add(cell4 , gbc); //------------------------- gbc.gridx = 2; gbc.gridwidth = 3; container.add(cell5 , gbc); //------------------------- gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.gridy = 2; gbc.gridx = 0; gbc.gridwidth = 5; container.add(cell6 , gbc); this.setLayout(new BorderLayout()); this.add(container , BorderLayout.CENTER); } }  

 
Voila qui peut être pratique non? C'est complexe à comprendre, complexe à utiliser, mais cela permet de faire les GUI les plus élaborés et les plus complets !
Comme ce layout propose de très nombreuses possibilités, je vous conseille fortement d'aller lire la page de documentation qui en parle. Avec ça, vous saurez tout !
 
Voila c'est tout pour aujourd'hui! Vous êtes maintenant capable de disposer vos composants exactement comme vous le souhaitez! Dans le prochain chapitre, nous nous attaquerons à la programmation événementielle, et donc notamment comment interagir avec ses boutons !
 
 
Résumé :
 
Le CardLayout permet d'empiler, de superposer plusieurs panneaux de manière optimisée, et de pouvoir passer facilement d'un panneau à l'autre dans l'affichage. Chaque panneau ajouté avec un CardLayout doit être ajouté avec son identifiant sous forme de String. Le GridBagLayout est un layout permettant la création de grille dont chaque cellule peut avoir une taille différente, de la même manière qu'un tableau Excel. Chaque composant doit être ajouté selon une contrainte du type GridBagConstraints. On parcourt la grille à l'aide de coordonnées. On choisi la taille de chaque composant en unités de cellules avec gridwidth et gridheight.  
 
JeremGamer
 
 

SystemGlitch
Dans cette seconde partie, nous allons découvrir un nouvel objet: Graphics2D. Il s'agit d'une amélioration de l'objet Graphics. Il nous permettra d'aller encore plus loin dans la création visuelle. Il est capable de nombreuses choses donc je ne vous parlerais que quelques une d'entre elles.
 
Mais avant ça, voyons comment le récupérer :
  Graphics2D g2d = (Graphics2D) g.create(); //ou Graphics2D g2d = (Graphics2D) g;  
Nous allons commencer par peindre des dégradés. Pour cela nous aurons besoin d'un GradientPaint que nous appliquerons ensuite à notre Graphics2D.
Le principe est simple :
public void paintComponent(Graphics gr) { Graphics2D g2d = (Graphics2D)gr; GradientPaint gp = new GradientPaint(0, 0, Color.GREEN, 30, 30, Color.BLUE, true); g2d.setPaint(gp); g2d.fillRect(0, 0, this.getWidth(), this.getHeight()); }  
Voici le résultat :
 

 
Voici les arguments du constructeur de GradientPaint :
Position x où commence la première couleur Position y où comment la première couleur La première couleur Position x où commence la seconde couleur Position y où commence la seconde couleur La seconde couleur boolean définissant si le dégradé se répète ou non.  
Ce que nous faisons ensuite, c'est que nous l'appliquons à notre Graphics2D, dans une forme, ici il s'agit d'un rectangle, mais on aurait pu dessiner ce dégradé dans un disque par exemple !
g2d.fillOval(getWidth() / 2 - 35, getHeight() / 2 - 35, 70, 70);  

 
Ne perdez pas d'esprit que les coordonnées du dégradés sont relatives au panneau sur lequel il est peint et non à sa forme !
 
Revenons à notre rectangle de base et voyons ce que ça donne en choisissant de ne pas répéter le dégradé :
 

 
La seconde couleur sera peinte jusqu'au bout de la forme, pensez donc à adapter votre forme !
 
J'imagine que vous vous êtes rendus compte que le dégradé était oblique, cela est du à nos coordonnées. Regardez, si on fait partir les deux couleurs depuis y=0, on aura un dégradé vertical :
GradientPaint gp = new GradientPaint(0, 0, Color.GREEN, 30, 0, Color.BLUE, true);  

 
Passons à autre aspect maintenant, en revenant à notre disque. Vous remarquerez que ce n'est pas joli joli et on constate que les courbes sont formées de telle sorte que des "escaliers" soient visibles. Nous n'avons pas appliqué d'anti-crénelage (antialiasing en anglais). Vous avez très certainement déjà rencontré ce phénomène qui est présent surtout dans les jeux vidéos, ces "escaliers" sont visibles sur les discontinuités entre deux objets, pour contrer cela, on utilise l'antialiasing.
En 3D, la technique la plus efficace, appelée "MSAA" (MultiSample AntiAlisasing), consiste à calculer l'image dans une résolution bien supérieure à celle de l'écran sur laquelle elle sera affichée puis la réduire ensuite afin de l'adapter au moniteur. Cette technique demande beaucoup de ressources cependant donc sur les gros jeux, il faudra une très bonne carte graphique pour pouvoir jouer fluidement avec ce paramètre.
 
Pour revenir à notre petite fenêtre sans prétention, la technique d'antialiasing que nous allons utiliser ici consiste en ajouter des pixels de la même couleur que la forme à dessiner, mais avec un certain niveau d'alpha, c'est à dire de transparence. Petite parenthèse d'ailleurs pour vous montrer comment créer votre propre couleur :
Color color = new Color(R , V , B , A); //Rouge, Vert, Bleu, Alpha (Alpha est optionnel) //N'oubliez pas que les valeurs seront des entiers allant de 0 à 255!  
Revenons à notre antialisasing. Voici donc comment il va fonctionner:
 

 
Vous voyez les pixels à moitié transparents autour de ce cercle? C'est bien de ceux-là que je vous parlais. Si on réduit l'image un grand nombre de fois, vous verrez un cercle parfait, or il ne l'est pas vraiment comme vous pouvez le voir ici! Cependant sans ces pixels transparents, le cercle apparaitrait comme dans l'exemple un peu plus haut.
 
Trêve de bavardages, voici comment faire !
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  
Résultat :
 

 
Faisons encore plus fou! Nous allons faire ce que l'on appelle un "Clipping". C'est à dire que nous allons récupérer qu'une petite partie de l'image pour ensuite effectuer des traitements dessus. Ce qui est génial c'est que l'on est pas obligé de récupérer une zone rectangulaire, n'importe quelle forme fonctionne! Ici nous allons remplir un cercle dans la région dans laquelle il est en contact avec un rectangle :
Graphics2D g2d = (Graphics2D)gr; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Shape oldClip = g2d.getClip(); //On récupère la région du rendu précédent Rectangle rect = new Rectangle(getWidth() / 2 - 80, 10, 80, getHeight() - 20); //On créé un rectangle grâce aux classes héritant de Shape Ellipse2D circle = new Ellipse2D.Double(getWidth() / 2 - 20, getHeight() / 2 - 20, 40, 40); //De même ici AffineTransform tx = new AffineTransform(); GeneralPath path = new GeneralPath(); path.append(tx.createTransformedShape(rect), false); //On applique la trransformation au rectangle via le chemin général de la région g2d.clip(circle); g2d.clip(path); g2d.fill(circle); g2d.setClip(oldClip); g2d.draw(circle); g2d.draw(path); //On dessine le chemin de la région  

 
Vous avez constaté ici qu'on a réalisé une transformation affine. Bien d'autres choses sont possibles grâce à elles !
Par exemple il est possible d'effectuer une rotation ! Exemple :
Graphics2D g2d = (Graphics2D)gr; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //Toujours l'antialisasing pour faire plaisir à vos mirettes Rectangle rect = new Rectangle(getWidth() / 2 - 60, 20, 60, getHeight() - 40); AffineTransform tx = new AffineTransform(); tx.rotate(0.5f, rect.getCenterX(), rect.getCenterY()); //On effectue une rotation en prenant le centre du rectangle comme axe g2d.draw(tx.createTransformedShape(rect)); //On dessine la forme transformée  

 
Et voila !
 
Sachez également qu'il est possible de "changer de crayon" avec Graphics2D. Par exemple, vous pouvez changer la couleur, l'épaisseur du trait, faire en sorte que le trait ne soit pas plein, etc... Pour cela nous utiliserons BasicStroke.
BasicStroke stroke = new BasicStroke(2.0f, //Largeur du trait BasicStroke.CAP_BUTT, //Contours des traits BasicStroke.JOIN_MITER, //Coins reliés 10.0f, //Limite new float[]{10.0f}, //Longueur du trait et des écarts, vous pouvez en mettre plusieurs, ils seront appliqués en alternance 0.0f); //Quand commence le premier trait g2d.setStroke(stroke); g2d.setColor(Color.RED); g2d.drawRoundRect(10, 10, 80, 60, 15, 15);  

 
Voyons un peu les variantes: il nous faudra faire varier le CAP et le JOIN.
 
 
 
CAP.ROUND : Les traits auront des contours arrondis : (j'ai grossis l'épaisseur des traits pour mieux s'en rendre compte)
 

 
 
 
CAP.SQUARE : Comme CAP.BUTT sauf que l'intégralité du traits aura des contours: (On le voit d'ailleurs au niveau des coins)
 

 
 
 
JOIN_BEVEL : Les coins seront aplatis : (C'est léger)
BasicStroke stroke = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); g2d.setStroke(stroke); g2d.setColor(Color.RED); g2d.drawRect(10, 10, 80, 60);  

 
 
 
JOIN_MITER : Les coins sont reliés proprement :
 

 
 
 
JOIN_ROUND : Les coins seront arrondis (Léger également) :
 

 
 
 
Et pour finir ce chapitre, je vais vous apprendre à remplir une zone avec une texture !
 
Il n'y a rien de bien compliqué, pour commencer, récupérez votre image comme dans le chapitre précédent puis faites ceci :
TexturePaint texture = new TexturePaint(img, new Rectangle(0, 0, 60, 60)); //les autres formes fonctionnent aussi g2d.setPaint(texture); g2d.fillRect(0, 0, getWidth(), getHeight()); //Le rectangle dans lequel peindre  
Faites bien attention à une chose, les valeurs données dans le Rectangle utilisé pour instancier notre TexturePaint seront celles utilisées pour la texture en elle-même et non pour sa forme, comme pour le dégradé ! Ici on dessinera la même texture mesurant 60*60 pixels sur toute la surface du panneau, elle sera donc dessinée plusieurs fois si la fenêtre est plus grande que 60*60 pixels.
 

 
Génial n'est-ce pas? Vous allez pouvoir commencer à faire des choses intéressantes avec tout ça ! Cependant ne vous relâchez pas trop vite, de nouvelles perspectives vont s'ouvrir à vous dès le prochain chapitre ! Je pense qu'un résumé n'est pas nécessaire pour ce chapitre sachant qu'il n'y a pas de réelles notions dedans. Amusez-vous bien avec vos nouvelles compétences en graphisme !
 
 
JeremGamer