FAQ JavaFXConsultez toutes les FAQ

Nombre d'auteurs : 4, nombre de questions : 507, dernière mise à jour : 2 novembre 2016  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur le forum JavaFX de http://java.developpez.com ainsi que l'expérience personnelle des auteurs.

Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.


SommaireSceneGraphTransfert de donnéesDrag'n Drop (10)
précédent sommaire suivant
 

Le drag'n drop ou DnD (littéralement tirer et lâcher, nommé en français cliquer-tirer) est un mécanisme qui permet par une action à la souris ou par un geste tactile d'activer le transfert d'un objet, ou une de ses représentations, vers une autre partie de l'interface graphique, un autre programme (Java ou même natif), des éléments du système sous-jacent (ex. : le bureau).

Il ne s'agit pas, ici, de vous apprendre comment cliquer sur un nœud pour le déplacer sur l’écran. Ici, nous verrons comment transférer des informations (qui peuvent être le nœud lui-même) vers des entités intéressées pour recevoir de telles informations.

Nous pouvons donc avoir :

  • nœud source → nœud destination ;
  • nœud source → application externe.

Mis à jour le 15 mars 2015 bouye

Pour déclencher le drag'n drop, il faut invoquer la méthode startDragAndDrop() de la classe Node par exemple, en réaction à une action de la souris sur un nœud.

La méthode startDragAndDrop() prend en paramètre une ou plusieurs instances de l’énumération javafx.scene.input.TransferMode :

  • TransferMode.COPY - la source du DnD indique qu'elle sera copiée dans sa destination ;
  • TransferMode.LINK - la source du DnD indique qu'elle sera liée dans sa destination ;
  • TransferMode.MOVE - la source du DnD indique qu'elle sera déplacée vers sa destination.


De plus, l’énumération propose des constantes prêtes à l'emploi pour un usage dans la méthode startDragAndDrop() :

  • TransferMode.ANY - tableau contenant COPY, LINK et MOVE → tous les modes de transfert sont supportés ;
  • TransferMode.COPY_OR_MOVE - tableau contenant COPY et MOVE → le mode LINK n'est pas supporté ;
  • TransferMode.NONE - tableau vide → aucun mode de transfert n'est supporté.


La méthode startDragAndDrop() retourne un objet de type javafx.scene.input.Dragboard. Cette classe fonctionne de manière similaire à la classe Clipboard qui gère le presse-papier : elle va servir à faire transiter des informations serializable qui peuvent être de plusieurs types. Le DnD ne démarrera pas si le contenu du Dragboard reste vide.

Par exemple, pour lancer le DnD lors d'une action à la souris, il suffit de fournir un écouteur de type EventHandler<MouseEvent> dans la propriété onDragDetected d'un nœud. Ce callback sera invoqué lorsqu'on clique sur le nœud et qu'on essaie de le tirer dans une direction. Ce callback n'est pas directement lié au DnD, il permet juste de le déclencher ; on pourrait tout aussi bien faire quelque chose de similaire avec la gestion tactile.

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Main extends Application { 
  
    @Override 
    public void start(Stage primaryStage) { 
        final Rectangle rectangle = new Rectangle(50, 50, 150, 100); 
        rectangle.setFill(Color.RED); 
        rectangle.setOnDragDetected(mouseEvent -> { 
            System.out.println("DnD detecté."); 
            final Dragboard dragBroard = rectangle.startDragAndDrop(TransferMode.COPY); 
            // Remlissage du contenu. 
            final ClipboardContent content = new ClipboardContent(); 
            // Exporter en tant que texte. 
            content.putString("Un rectangle rouge."); 
            // Exporter en tant que couleur ARGB. 
            DataFormat paintFormat = DataFormat.lookupMimeType(Paint.class.getName()); 
            paintFormat = (paintFormat == null) ? new DataFormat(Paint.class.getName()) : paintFormat; 
            final Color color = (Color) rectangle.getFill(); 
            final int red = (int) (255 * color.getRed()); 
            final int green = (int) (255 * color.getGreen()); 
            final int blue = (int) (255 * color.getBlue()); 
            final int alpha = (int) (255 * color.getOpacity()); 
            final int argb = alpha << 24 | red << 16 | green << 8 | blue << 0; 
            content.put(paintFormat, argb); 
            // Exporter en tant qu'image.        
            final WritableImage capture = rectangle.snapshot(null, null); 
            content.putImage(capture); 
            // 
            dragBroard.setContent(content); 
            mouseEvent.consume();         
        }); 
        final Pane root = new Pane(); 
        root.getChildren().setAll(rectangle); 
        final Scene scene = new Scene(root, 600, 600); 
        primaryStage.setTitle("Test de DnD"); 
        primaryStage.setScene(scene); 
        primaryStage.show(); 
    } 
  
    public static void main(String[] args) { 
        launch(args); 
    } 
}

Si vous cliquez et tirez votre rectangle, le message « DnD détecté » sera affiché sur la console. Mais surtout, nous démarrons le support du DnD en indiquant que notre source de données souhaite être copiée à destination.

Nous avons de plus exporté notre rectangle sous diverses formes :
  • une chaine de caractères donnant sa description ;
  • sa peinture de remplissage sous forme d'entier ARGB. Ici, la classe Color de l'API JavaFX n’étant pas serializable, nous ne pouvons pas la placer directement dans le DragBoard ;
  • une image du rectangle.


Si vous avez une version récente de JavaFX, lorsque vous cliquez et tirez sur votre rectangle, vous apercevrez une version transparente du rectangle qui se déplacera en suivant le curseur de votre souris. Sur des versions plus anciennes, rien ne s'affichera.

Le curseur de votre souris lui-même se transformera lorsque vous passez au-dessus d'autres nœuds JavaFX pour indiquer qu'aucun n'accepte le DnD.

Vous pouvez également poursuivre le DnD hors des limites de votre application. Si vous relâchez le bouton de votre souris sur un éditeur de texte (ex. : sous Windows, le bloc-note ou Microsoft Word), la chaine de caractères « Un rectangle rouge » sera collée dans votre document courant. Certains logiciels d’édition bitmap peuvent même aller jusqu’à accepter l'image de votre rectangle (ex. : sous Windows, cela fonctionne avec Adobe Photoshop, mais pas avec Paint.NET, Paint ou Inkscape).

Mis à jour le 15 mars 2015 bouye

À partir du moment où le drag'n drop s'est déclenché (voir conditions plus haut), n'importe quel nœud du SceneGraph, y compris le nœud source, devient une destination potentielle et peut recevoir des événements de notification en enregistrant des écouteurs de type EventHandler<DragEvent> via les callbacks suivants :

  • onDragDropped - le geste de DnD a été relâché au-dessus du nœud (qui devient le nœud destination) ;
  • onDragEntered - le geste de DnD est entré sur la surface du nœud ;
  • onDragExited - le geste de DnD est sorti de la surface du nœud ;
  • onDragOver - le geste de DnD se trouve au-dessus du nœud.


Ces callbacks sont invoqués sur les cibles potentielles du DnD qui se trouvent sous le curseur de la souris. Ils sont également invoqués lorsque la source du DnD se trouve être à l’extérieur de l'application JavaFX. Par exemple, si on initie un cliquer-tirer d'un paragraphe de texte (ex. : dans Microsoft Word ou OpenOffice Writer), il est tout à fait possible de recevoir ces événements si on déplace le curseur au-dessus d'une application JavaFX.

Lorsque le DnD est terminé, la source (s'il s'agit d'une source JavaFX bien sûr) recevra une notification via le callback onDragDone.

Mis à jour le 15 mars 2015 bouye

Pour connaitre la source du drag'n drop, il suffit d'invoquer la méthode getGestureSource() de l’objet de type DragEvent passé en paramètre des callbacks.

Par exemple :

Code Java : Sélectionner tout
final Object source = dragEvent.getGestureSource();

Mis à jour le 15 mars 2015 bouye

Pour connaitre la destination du drag'n drop, il suffit d'invoquer la méthode getGestureTarget() de l’objet de type DragEvent passé en paramètre des callbacks placés sur le nœud destination.

Code Java : Sélectionner tout
final Object source = dragEvent.getGestureTarget();

Tant que le DnD n'est pas validé, finalisé ou s'il est annulé (en appuyant sur la touche ECHAP par exemple), cette méthode retournera la valeur null.

Mis à jour le 15 mars 2015 bouye

Pour valider le fait qu'un nœud destination peut accepter un (ou plusieurs) type(s) de données et un (ou plusieurs) mode(s) de transfert, vous devez invoquer la méthode acceptTransferModes() de la classe DragEvent.

Ajoutons un cercle bleu dans notre exemple :

Code Java : Sélectionner tout
1
2
3
final Circle circle = new Circle(250, 200, 50); 
circle.setFill(Color.BLUE); 
root.getChildren().add(circle);

Pour le moment, quand la souris passe au-dessus du cercle lors du DnD, son curseur indique qu'il n'accepte pas les données que nous essayons de transférer.

Nous allons lui fournir un écouteur de type EventHandler<DragEvent> dans le callback onDragOver, ce qui va permettre de changer ce comportement :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
circle.setOnDragOver(dragEvent -> { 
    final Dragboard dragBroard = dragEvent.getDragboard(); 
    DataFormat paintFormat = DataFormat.lookupMimeType(Paint.class.getName()); 
    paintFormat = (paintFormat == null) ? new DataFormat(Paint.class.getName()) : paintFormat; 
    if (dragEvent.getGestureSource() != circle && dragBroard.hasContent(paintFormat)) { 
        // Indique les modes de transfert autorisés sur cette destination. 
        dragEvent.acceptTransferModes(TransferMode.COPY); 
    } 
    dragEvent.consume(); 
});

Ici, nous avons vérifié que la source du DnD n'est pas notre cercle lui-même. Puis, nous avons vérifié que le DragBoard contient bien une donnée dans un format qui nous intéresse. Puis, nous avons indiqué que le cercle supporte les modes de transfert de type COPY.

Désormais, quand la souris passe au-dessus du cercle lors du DnD, son curseur indique que le cercle accepte les données que nous essayons de transférer.

Mis à jour le 15 mars 2015 bouye

Pour récupérer la donnée dans la destination, vous devez fournir un écouteur de type EventHandler<DragEvent> dans le callback onDragDroppped du nœud destination. Ce callback sera invoqué uniquement lorsque le DnD est relâché au-dessus du nœud destination et uniquement si ce nœud a auparavant validé les types de données et les modes de transfert du DnD.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
circle.setOnDragDropped(dragEvent -> { 
    boolean success = false; 
    try { 
        final Dragboard dragBroard = dragEvent.getDragboard(); 
        DataFormat paintFormat = DataFormat.lookupMimeType(Paint.class.getName()); 
        paintFormat = (paintFormat == null) ? new DataFormat(Paint.class.getName()) : paintFormat; 
        final int argb = (Integer) dragBroard.getContent(paintFormat); 
        final double opacity = ((argb >> 24) & 0xFF) / 255.0; 
        final int red = (argb >> 16) & 0xFF; 
        final int green = (argb >> 8) & 0xFF; 
        final int blue = (argb >> 0) & 0xFF; 
        final Color fill = Color.rgb(red, green, blue, opacity); 
        circle.setFill(fill); 
        success = true; 
    } catch (Exception ex) { 
       [...] 
    } finally { 
        dragEvent.setDropCompleted(success); 
        dragEvent.consume(); 
    } 
});

Ici, nous récupérons la couleur qui a été placée dans le DragBoard au tout début du DnD et, après décodage, nous l'affectons en tant que couleur de remplissage de notre cercle. Lorsque le DnD est relâché au-dessus du cercle bleu, ce dernier prend la couleur rouge.

Lorsque l’opération est finie, la méthode setDropCompleted() de la classe DragEvent doit être invoquée avec le paramètre true pour signifier que le DnD est terminé.

Mis à jour le 15 mars 2015 bouye

Il est possible de faire que le nœud destination présente un visuel indiquant si le transfert est validé ou pas en utilisant ses callbacks onDragEntered et onDragExited. Ces callbacks acceptent des écouteurs de type EventHandler<DragEvent>.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
circle.setOnDragEntered(dragEvent -> { 
    final Dragboard dragBroard = dragEvent.getDragboard(); 
    DataFormat paintFormat = DataFormat.lookupMimeType(Paint.class.getName()); 
    paintFormat = (paintFormat == null) ? new DataFormat(Paint.class.getName()) : paintFormat; 
    if (dragEvent.getGestureSource() != circle && dragBroard.hasContent(paintFormat)) { 
        circle.setStroke(Color.GREEN); 
    } else { 
        circle.setStroke(Color.ORANGE); 
    } 
    circle.setStrokeWidth(10); 
    dragEvent.consume(); 
}); 
circle.setOnDragExited(dragEvent -> { 
    circle.setStroke(null); 
    circle.setStrokeWidth(0); 
    dragEvent.consume(); 
});

Ici, lorsque le geste de notre DnD passe au-dessus du cercle, le nœud se pare d'une bordure verte ou orange selon que le transfert est validé ou pas. Lorsque le DnD sort de la surface du nœud (ou qu'il est activé), la bordure disparait.

De la même manière, il est également possible d'utiliser des CSS inline, des styles de classe ou des pseudoclasses de manière à modifier l'apparence visuelle du nœud destination en fonction du contenu du DnD.

Mis à jour le 15 mars 2015 bouye

Si le drag'n drop a été déclenché à partir d'un nœud SceneGraph, alors le callback onDragDone sera invoqué sur le nœud source pour finaliser le transfert lorsque le geste de DnD est relâché sur un nœud destination ou une application externe. Ce callback accepte des écouteurs de type EventHandler<DragEvent>.

Code Java : Sélectionner tout
1
2
3
4
5
6
7
source.setOnDragDone(dragEvent -> { 
    if (dragEvent.getTransferMode() == TransferMode.MOVE) { 
        // Faire ce qui est nécessaire pour retirer la source ou la donnée. 
        [...] 
    } 
    dragEvent.consume(); 
});

Ce callback est invoqué même si la destination n'accepte pas le DnD, il est donc important de tester le mode de transfert qui a été utilisé avant d'agir.

Mis à jour le 15 mars 2015 bouye

Actuellement, utiliser le type de données DataFormat.IMAGE accompagné d'une Image au format JavaFX ne permet pas d'importer l'image fournie dans la plupart des logiciels ou dans le système (ex. : durant mes tests, sous Windows, cela ne fonctionne qu'avec Adobe Photoshop).

Il existe cependant une solution qui consiste à exporter l'image JavaFX vers un fichier temporaire en utilisant Swing et ImageIO :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
try { 
    final File tempFile = File.createTempFile(String.format("export-%d", System.currentTimeMillis()), ".png"); 
    System.out.println(tempFile.getAbsolutePath()); 
    try (final OutputStream output = new FileOutputStream(tempFile)) { 
        final BufferedImage swingImage = SwingFXUtils.fromFXImage(fxImage, null); 
        ImageIO.write(swingImage, "png", output); 
        content.putFiles(Arrays.asList(tempFile)); 
        tempFile.deleteOnExit(); 
    } 
} catch (IOException ex) { 
    Logger.getLogger(TestDnD.class.getName()).log(Level.SEVERE, null, ex); 
}

Ici, nous créons un fichier temporaire dans lequel nous exportons au format PNG une image Swing créée à partir de notre image JavaFX. Nous plaçons ensuite l'objet File qui référence ce fichier dans le contenu à exporter. Ce faisant, la plupart des logiciels de dessin sous Windows peuvent désormais ouvrir l'image puisqu'ils essaieront d'utiliser le chemin du fichier reçu lors de l’opération de DnD. Il est également possible de copier l'image en effectuant un DnD directement vers le bureau de cette manière.

Note : cette méthode ne fonctionnera que sur les systèmes qui supportent également Swing et ImageIO.

Une procédure similaire peut également être développée pour exporter du contenu en HTML, RTF, etc. vers les logiciels appropriés.

Mis à jour le 15 mars 2015 bouye

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 -