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 : 2018/01/29

Notions : JavaFX, JavaFX Application thread

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 !

  • Le nombre de composants à afficher est MAX une constante de la classe. Plus elle est grande, plus le composant sera long à afficher
  • Vous avez besoin d'un générateur de nombres aléatoires, je vous propose de faire un attribut de classe de type Random puis d'utiliser les méthodes comme rand.nextQuelquechose()
  • Ne pas oublier d'ajouter un menu avec au moins deux items : le premier pour dessiner 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 un ralentissement ou un blocage de l'interface !

    Une interface non bloquée

    Groupe de formes

    Si ce sont les rectangles d'un Group qui sont affichés, l'idée est de créer un nouveau groupe en arrière-plan puis de remplacer quand c'est terminé !

    Pour dessiner en arrière-plan, il est nécessaire de créer une tâche idoine, qui par exemple ne reverrait rien (représenté par le type objet Void)

    
    Task task = new Task<Void>() {
      
            @Override public Void call() {
                
                Group group = new Group();
                for (int i=1; i<=MAX; i++) {
                    if (isCancelled())
                       break;
                    
                    // ce qu'il y a à faire
                    // mise à jour de la barre de progression         
                }
              
                // remplacer le noeud
                return null;
            }
        };
    

    Pour changer de noeud, il faut synchroniser l'opération avec le thread d'application JavaFX

    
    Platform.runLater(()->root.setCenter(group));
    

    Il reste encore à lancer cette tâche en arrière-plan car sinon cela ne sert à rien :

    
    new Thread(task).start();
    

    Il ne faut surtout pas appeler la méthode call() de la tâche directement.

    Si vous avez l'idée d'ajouter une barre de progression à l'affichage ProgressBar

    , sachez qu'une tâche à une propriété de progression qu'il est très facile de lier avec celle de la barre de progression :
    
    progress.progressProperty().bind(task.progressProperty());
    

    C'est d'ailleurs avec ce genre de relation qu'il est possible de lier la taille (largeur préférée) de la barre de progression à la largeur de la fenêtre :

    
    progress.prefWidthProperty().bind(root.widthProperty());
    

    Utilisation d'un Canvas

    En JavaFX, contrairement à Swing, il n'est pas possible de dessiner dans une image directement pour ensuite placer cette image à l'écran. Cependant, tous les composants JavaFX disposent d'une méthode snapshot() qui permet de généner une image qui les représente. Ma solution utilise donc les composants suivants :

    Le seul bémol de cette méthode est qu'il faut faire le snapshot dans le thread de l'application. La méthode runAndWait() n'existe pas alors voilà un exemple de code que j'ai trouvé (*)

    
      public static void runAndWait(Runnable action) {
        if (action == null)
            throw new NullPointerException("action");
    
        // run synchronously on JavaFX thread
        if (Platform.isFxApplicationThread()) {
            action.run();
            return;
        }
    
        // queue on JavaFX thread and wait for completion
        final CountDownLatch doneLatch = new CountDownLatch(1);
        Platform.runLater(() -> {
            try {
                action.run();
            } finally {
                doneLatch.countDown();
            }
        });
    
        try {
            doneLatch.await();
        } catch (InterruptedException e) {
            // ignore exception
        }
    }
    

    On peut aussi attendre "bêtement" après un runLater() que l'image soit disponible.

    Vous pouvez ajouter une barre de progression comme au premier point et lier la tâche en arrière-plan de génération de formes avec cette barre de progression.

    Le résultat est bien meilleur / fluide avec cette version notamment au redimensionnement de la fenêtre.