tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[JavaSE] Picasso

Date de première publication : 2016/02/09

Notions : Swing, thread, fil des événements

Le but de l'exercice est de montrer qu'une interface graphique simple peut se figer facilement mais surtout que l'on peut faire plusieurs choses en même temps (multithreading).

Voici la correction pas à pas (ou presque) de l'exercice intégré au support de cours...

diagramme

Une interface qui se fige

Il faut un composant graphique capable d'afficher des rectangles, des ellipse ou des polygones, ou même tout en même temps, au choix !

class JCanvas extends JPanel {

   public void paintComponent(Graphics g) {
      super.paintComponent(g);

      for(i = 0; i< MAX; ++i ) {
         g.setColor(...);
         g.fillRect(...);
    }
  }
  
}

Ne pas oublier d'ajouter un menu avec au moins deux items : le premier pour redessiner l'interface et le second pour quitter.

Pour une grande valeur de MAX (et surtout avec un modèle de couleurs ARGB), vous devez constater le blocage de l'interface !

Une interface non bloquée

L'idée est de dessiner dans une mémoire tampon en arrière-plan et de l'afficher quand elle est terminée

Dessiner dans une image

Cela ne va pas changer grand-chose (enfin si, on va encore plus ralentir le processus) mais cela va vous permettre de vérifier que l'on dessine correctement dans l'image

class JCanvas extends JPanel {

   public void paintComponent(Graphics g) {
      super.paintComponent(g);

      BufferedImage image = new BufferedImage(...);
      Graphics      g2    = image.getGraphics();

      for(i = 0; i< MAX; ++i ) {
         g2.setColor(...);
         g2.fillRect(...);
      }

      g.drawImage(image, 0, 0, null);
   }
  
}

L'idée est très simple : créer une image de la taille de la fenêtre, récupérer le contexte graphique associé à cette image et dessiner dedans

BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);

Il faut juste ne pas oublier de dessiner l'image sur l'écran.

Dessiner en arrière-plan

On veut dessiner une image en arrière-plan, alors voilà ce que je propose, on va doter le canvas d'un attribut image et le canvas sera la tâche qui permet de dessiner dedans

class Canvas extends JPanel implements Runnable {
   private BuffredImage image;
}

Si l'image est non nulle, on va l'afficher sinon on va la calculer :

public void paintComponent(Graphics g) {
   if (image==null)
      (new Thread(this)).start();
    else g.drawImage(image, 0, 0, null); 
}

Vous pouvez améliorer cette méthode en affichant un petit quelquechose pour l'utilisateur (calcul en cours, lancer le calcul ...)

Il manque quand même la réalisation du calcul (la fameuse tâche à faire en fond)

public void run() {}
   BufferedImage temp = new BufferedImage(...);
   // le code du paintComponent() d'avant;
   image = temp;
   repaint();   
}

Vous voulez redessiner l'image ? Il faut mettre l'attribut à null ...

Vous pouvez améliorer l'affichage en précisant que le calcul est en cours ou bien proposer une barre de progression.

Je me suis amusé à mettre un conteneur en bas de la fenêtre qui ajoute une barre de progression pour chaque calcul en cours :-)

Le reste

Le redimensionnement de la fenêtre

On va maintenant s'occuper du redimensionnement de la fenêtre. Quel est le listener qui gère le redimensionnement ?

ComponentListener

mais il faut d'abord choisir le comportement que l'on veut : afficher un morceau du dessin si la fenêtre est réduite, calculer un nouveau dessin ou encore appliquer un ratio quelque soit la nouvelle taille...

Si vous choisissez cette dernière option, pas besoin du listener, une option de drawImage() suffira !

public void paintComponent(Graphics g) {
   if (image==null)
      (new Thread(this)).start();
    else g.drawImage(image, 0, 0, getWidth(), getHeight(), null); 
}

Un seul code

Il reste encore à défnir la sortie du programme en un seul point : un clic sur la croix et sur l'item de menu ...

C'est encore trop flou ? Vous voulez la correction ?

Le fichier s'appelle RectApp.java