Nous expliquons ici comment proposer du drag and drop pour un type non standard et un objet graphique non standard
Le type que nous allons exporter par drag and drop est la classe Personne. Une personne est simplement définie par un nom et un prenom. Cependant, nous verrons que la complexité de la classe à exporter n'a que très peu d'importance.
Nous créerons un objet graphique swing capable d'afficher des personnes et de les exporter ou importer par drag and drop.
Les formats sont identifiés par des DataFlavor. Un DataFlavor est juste un objet java qui représente un type MIME. Ce n'est qu'un identifiant, par lui-même il ne sait rien faire.
Pour information : les types MIME permettent à des logiciels (comme les lectueurs de mail, les navigateurs web...) de reconnaître les types des fichiers. Par exemple, un fichier html sera identifié comme : "text/html" ; un fichier image pourra être décrit comme "image/png", et un fichier pdf comme "application/pdf".
La classe DataFlavor elle même contient des constantes pour les types les plus courants, comme DataFlavor.stringFlavor. Pour identifier notre type Personne, nous utiliserons un DataFlavor défini ainsi :
String MIME= DataFlavor.javaJVMLocalObjectMimeType + ";class=" + Personne.class.getName(); DataFlavor personneFlavor = new DataFlavor(MIME);Voici intégralement le texte de notre classe :
public class PersonneTransferable implements Transferable { /** * La personne à transférer. */ private Personne personne; /** * @param p */ public PersonneTransferable(Personne p) { personne= p; } /** * La liste des flavor supportées, pour que les cibles potentielles sachent si elles * peuvent recevoir la donnée. * *Les flavor sont données par ordre de préférence. */ public DataFlavor[] getTransferDataFlavors() { DataFlavor[] result= new DataFlavor[2]; // D'abord, la PersonneFlavor (on l'a stockée comme constante dans PersonneFlavorFactory, // qui ne sert qu'à ça. result[0]= PersonneFlavorFactory.getPersonneFlavor(); // Ensuite, le texte bête. result[1]= DataFlavor.stringFlavor; return result; } /** * Dit si nous acceptons de retourner une flavor particulière. */ public boolean isDataFlavorSupported(DataFlavor flavor) { return PersonneFlavorFactory.getPersonneFlavor().equals(flavor) || DataFlavor.stringFlavor.equals(flavor); } /** * La récupération des données proprement dite. *
Renvoie un objet selon la flavor demandée (Personne ou String). */ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (PersonneFlavorFactory.getPersonneFlavor().equals(flavor)) return personne; else if (DataFlavor.stringFlavor.equals(flavor)) { return personne.getNom().toUpperCase()+ " " + personne.getPrenom(); } else throw new UnsupportedFlavorException(flavor); } }
public class PersonneTransferHandler extends TransferHandler { /** * Crée un objet transférable adapté, donc un PersonneTransferable. * Reçoit comme paramètre la source du drag and drop * (ce qui permet de partager des PersonneTransferHandler) */ protected Transferable createTransferable(JComponent c) { PersonnePanel panel= (PersonnePanel) c; Personne p= panel.getPersonne(); return new PersonneTransferable(p); } /** * Permet de dire si le drag est possible. Dans notre cas, il l'est toujours. * cette méthode peut renvoyer COPY, NONE, MOVE, COPY_OR_MOVE. * Si NONE est renvoyé, le drag and drop sera impossible. */ public int getSourceActions(JComponent c) { return COPY; } }Notez que la classe TransferHandler a une méthode
getVisualPresentation
. Cette dernière n'est
jamais appelée.
setTransferHandler(new PersonneTransferHandler());
public class DragAndDropListener extends MouseAdapter { public void mousePressed(MouseEvent e) { PersonnePanel panel= (PersonnePanel) e.getSource(); panel.getTransferHandler().exportAsDrag(panel, e, TransferHandler.COPY); e.consume(); // empêche que l'événement soit transmis au container du PersonnePanel. } }Et c'est tout pour le drag ! Avec ce code là, une personne peut déjà être recopiée dans openoffice par drag and drop. Elle apparaîtra comme du texte.
Par contre, lors du drop, la cible va devoir dire à l'objet transferable qui lui est présenté quels types de données elle peut comprendre. Tout cela se fait dans le TransferHandler, qui est doté des méthodes supplémentaires :
/** * Teste si les données proposée comportent une PersonneFlavor. * On pourrait aussi accepter des données de mode texte, par exemple. */ public boolean canImport(JComponent c, DataFlavor[] flavors) { for (int i = 0; i < flavors.length; i++) { if (PersonneFlavorFactory.getPersonneFlavor().equals(flavors[i])) { return true; } } return false; } /** * L'importation de données proprement dite. * * @param c : la cible du transfert. * @param t : données à transférer */ public boolean importData(JComponent c, Transferable t) { if (canImport(c,PersonneFlavorFactory.getPersonneFlavor())) { try { // On extrait l'objet Personne du transferable Personne p= (Personne) t.getTransferData(PersonneFlavorFactory.getPersonneFlavor()); // On le range dans la cible. ((PersonnePanel)c).setPersonne(p); return true; } catch (UnsupportedFlavorException ufe) { ufe.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } } return false; }Le logiciel complet est téléchargeable ici (archive ici)
La première est que les éléments graphiques qui réalisent l'affichage soient des composants swing. Pour cela, il faudra écrire un layoutManager adapté.
La seconde est de jouer sur les classes de java.awt.dnd. Pour la partie "drag", les classes de swing sont déjà utilisables : en effet, au moment du drag, on sait où se trouve la souris. Il suffit de créer le Transferable en fonction des besoins (ce qui est éventuellement faisable en surchargeant exportAsDrag dans TransferHandler. Pour le drop, par contre, la version fournie par défaut est globale. Il faudra donc passer par les bibliothèques de bas niveau (ou alors avoir un système en deux temps : sélection de la zone cible, puis drag and drop. C'est ce que font les éditeurs de texte, par exemple : on "drop" à l'emplacement du curseur.
Pour les fonctionalités drag and drop de bas niveau, regarder http://www.javaworld.com/javaworld/jw-03-1999/jw-03-dragndrop_p.html, http://java.sun.com/products/jfc/tsc/articles/dragndrop/#adding_d_n_d et http://www.javaworld.com/javatips/jw-javatip97_p.html
(Documentation à venir.) Un exemple : la classe TargetedDragAndDrop.