Comment puis-je utiliser le glisser-déposer dans Swing pour obtenir le chemin du fichier?
-
03-07-2019 - |
Question
J’ai un JTextField dans mon application Swing qui contient le chemin du fichier du fichier sélectionné à utiliser. Actuellement, j'ai un JFileChooser utilisé pour renseigner cette valeur. Cependant, j'aimerais ajouter la possibilité pour un utilisateur de glisser-déposer un fichier sur ce JTextField et de le placer dans son chemin dans le JTextField au lieu de toujours utiliser JFileChooser.
Comment cela peut-il être fait?
La solution
D'abord, vous devriez vous renseigner sur le prise en charge de Swing DragDrop . . Après cela, il y a quelques petites astuces pour différents systèmes d'exploitation. Une fois que vous avez terminé, vous allez gérer le rappel drop (). Dans ce rappel, vous souhaiterez vérifier le DataFlavor du Transférable.
Pour Windows, vous pouvez simplement vérifier la DataFlavor.isFlavorJavaFileListType () , puis récupérez vos données comme ceci
List<File> dropppedFiles = (List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor)
Pour Linux (et probablement Solaris), DataFlavor est un peu plus compliqué. Vous devrez créer votre propre DataFlavor et le type Transférable sera différent
nixFileDataFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
String data = (String)transferable.getTransferData(nixFileDataFlavor);
for(StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();)
{
String token = st.nextToken().trim();
if(token.startsWith("#") || token.isEmpty())
{
// comment line, by RFC 2483
continue;
}
try
{
File file = new File(new URI(token))
// store this somewhere
}
catch(...)
{
// do something good
....
}
}
Autres conseils
Il existe un exemple de programme contenant une classe pouvant être utilisée pour faciliter le glisser-déposer des fichiers et des dossiers:
http://www.iharder.net/current/java/filedrop/
J'ai testé cela avec Windows 7 et Ubuntu 10.10, et cela semble bien fonctionner dans les deux environnements.
Pour l'utiliser, vous ajoutez quelque chose comme ceci à votre code:
JPanel myPanel = new JPanel();
new FileDrop( myPanel, new FileDrop.Listener()
{ public void filesDropped( java.io.File[] files )
{
// handle file drop
...
} // end filesDropped
}); // end FileDrop.Listener
Si vous ne souhaitez pas consacrer trop de temps à la recherche sur ce sujet relativement complexe et que vous utilisez Windows (ou Java 7 ou une version ultérieure), voici un exemple rapide expliquant comment accepter les fichiers supprimés avec un JTextArea une cible de largage:
JTextArea myPanel = new JTextArea();
myPanel.setDropTarget(new DropTarget() {
public synchronized void drop(DropTargetDropEvent evt) {
try {
evt.acceptDrop(DnDConstants.ACTION_COPY);
List<File> droppedFiles = (List<File>)
evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
for (File file : droppedFiles) {
// process files
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
Je sais que c'est une vieille question, mais les réponses actuelles sont un peu dépassées:
- depuis JDK 1.6, la classe 'TransferHandler' doit être utilisée avec les nouvelles méthodes (écrasées)
- la prise en charge de Linux s’est améliorée, pas besoin de gestion personnalisée
Ceci fonctionne sous Linux (KDE5) et Windows 7:
final class FileDropHandler extends TransferHandler {
@Override
public boolean canImport(TransferHandler.TransferSupport support) {
for (DataFlavor flavor : support.getDataFlavors()) {
if (flavor.isFlavorJavaFileListType()) {
return true;
}
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public boolean importData(TransferHandler.TransferSupport support) {
if (!this.canImport(support))
return false;
List<File> files;
try {
files = (List<File>) support.getTransferable()
.getTransferData(DataFlavor.javaFileListFlavor);
} catch (UnsupportedFlavorException | IOException ex) {
// should never happen (or JDK is buggy)
return false;
}
for (File file: files) {
// do something...
}
return true;
}
}
Utilisez-le sur n'importe quel composant avec
myComponent.setTransferHandler(new FileDropHandler());
Cela fonctionne pour moi. Je l'utilise comme ça (scala):
def onDrop(files: List[java.io.File]): Unit = { ... }
val lblDrop = new Label {
peer.setTransferHandler(new FileDropHandler(onDrop))
border = EtchedBorder
}
class FileDropHandler(val onDrop: List[java.io.File] => Unit) extends javax.swing.TransferHandler {
import javax.swing.JComponent
import java.awt.datatransfer.{Transferable, DataFlavor}
import java.net.URI
import java.io.File
val stdFileListFlavor = DataFlavor.javaFileListFlavor
val nixFileListFlavor = new DataFlavor("text/uri-list;class=java.lang.String")
override def canImport(comp: JComponent, flavors: Array[DataFlavor]): Boolean =
flavors.exists(flavor =>
(flavor == stdFileListFlavor) ||
(flavor == nixFileListFlavor)
)
override def importData(comp: JComponent, t: Transferable): Boolean = {
val flavors = t.getTransferDataFlavors()
val files = if (flavors.exists(_ == stdFileListFlavor)) {
val data = t.getTransferData(stdFileListFlavor)
importStdFileList( data )
} else if (flavors.exists(_ == nixFileListFlavor)) {
val data = t.getTransferData(nixFileListFlavor)
importNixFileList( data )
} else List()
onDrop( files )
!files.isEmpty
}
private def importStdFileList(data: Any): List[File] = {
data.asInstanceOf[List[File]] //XXX NOT TESTED
}
private def importNixFileList(data: Any): List[File] = {
def clean(rawLine: String): Option[String] = {
val line = rawLine.trim
if (line.length == 0 || line == "#") None
else Some(line)
}
def asURI(line: String): Option[URI] = {
try { Some(new URI(line)) }
catch { case e:Exception => println(e); None }
}
def asFile(uri: URI): Option[File] = {
try { Some(new File(uri)) }
catch { case e:Exception => println(e); None }
}
data.asInstanceOf[java.lang.String].split("\n")
.toList flatMap clean flatMap asURI flatMap asFile
}
}