48

I have a JTextField in my Swing application that holds the file path of a file selected to be used. Currently I have a JFileChooser that is used to populate this value. However, I would like to add the ability for a user to drag-and-drop a file onto this JTextField and have it place the file path of that file into the JTextField instead of always having using the JFileChooser.

How can this be done?

Abra
  • 19,142
  • 7
  • 29
  • 41
Ascalonian
  • 14,409
  • 18
  • 71
  • 103

5 Answers5

47

In case you don't want to spend too much time researching this relatively complex subject, and you're on Java 7 or later, here's a quick example of how to accept dropped files with a JTextArea as a drop target:

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();
        }
    }
});
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 2
    This should be the accepted answer. Two comments: first, you should check the type of the transfer data before accepting the drop and second, you should call `evt.dropComplete(true)` when you are done to complete any drop-related animation, otherwise, things look funny to the user even though the drop worked. – Christopher Schultz Jul 31 '18 at 21:39
45

First you should look into Swing DragDrop support. After that there are few little tricks for different operating systems. Once you've got things going you'll be handling the drop() callback. In this callback you'll want to check the DataFlavor of the Transferable.

For Windows you can just check the DataFlavor.isFlavorJavaFileListType() and then get your data like this

List<File> dropppedFiles = (List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor)

For Linux (and probably Solaris) the DataFlavor is a little trickier. You'll need to make your own DataFlavor and the Transferable type will be different

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
       ....
    }
}
basszero
  • 29,624
  • 9
  • 57
  • 79
  • 1
    +1 Thank you very much! :) However, [thankfully] this trick is not necessary with Java7. – Oleg Kuznetsov Mar 07 '12 at 17:27
  • @Oleg: With Java 7 do you mean that the DataFlavor will be javaFileListFlavor irrespective of platform? – Adamski Aug 22 '12 at 16:43
  • 6
    @Adamski Yes, with Java 7 JRE, usage of javaFileListFlavor is enough (at least on Windows and Linux). – Oleg Kuznetsov Sep 14 '12 at 16:58
  • After reading the DragDrop link, I found this page helped me support drag drop very efficiently for a custom JComponent of mine: https://docs.oracle.com/javase/tutorial/uiswing/dnd/toplevel.html (all I had to do was implement the handler) – Perry Monschau Mar 17 '18 at 00:49
34

There is an example program which contains a class which can be used for facilitating drag and drop for files and folders:

http://www.iharder.net/current/java/filedrop/

I tested this with both Windows 7 and Ubuntu 10.10, and it appears to work well in both environments.

To use it, you add something like this to your 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
Mr Ed
  • 5,068
  • 1
  • 19
  • 12
  • @Joe - From the website: "Any java.awt.Component can be dropped onto, but only javax.swing.JComponents will indicate the drop event with a changed border." - Given JTable is a JComponent, I'd say, yes, it'll completely work with it. – ArtOfWarfare Nov 30 '12 at 20:17
  • interesting, working again now. must be due to the latest windows update – phil294 Aug 22 '15 at 16:44
22

I know this is an old question but the current answers are all a bit outdated:

  • since JDK 1.6 the class 'TransferHandler' should be used with new (overwritten) methods
  • support for Linux became a lot better, no need for custom handling

This works on Linux (KDE5) and 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;
    }
}

Use it on any component with

myComponent.setTransferHandler(new FileDropHandler());
ABika
  • 597
  • 1
  • 5
  • 11
  • 1
    Tested it on my Mac with OS "High Sierra", works perfectly!! – trinity420 Jul 05 '18 at 15:31
  • In `canImport` you iterate over the data flavors checking `isFlavorJavaFileListType` for a supported flavor, but in `importData` you use `getTransferData(DataFlavor.javaFileListFlavor)` rather than iterating over all the flavors. Why not use `support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)` in `canImport` or iterate and do the same check in `importData`? – Rangi Keen Oct 26 '18 at 20:31
  • @RangiKeen Iterating and using `isFlavorJavaFileListType()` is easier - a one-liner with Stream API and no try-catch block is required. In `importData()` getting the `File`s is only possible with `getTransferData()`. – ABika Oct 28 '18 at 11:52
  • @ABika But why assume that the only flavor that returns true from `isFlavorJavaFileListType` is `DataFlavor.javaFileListFlavor`? Is it possible that your check in `canImport` would return true when there is no transfer data for `DataFlavor.javaFileListFlavor`? I'd expect that if you iterate to find the supported flavor in `canImport` that you'd also iterate to find the same flavor in `importData` and then use that to get the transfer data rather than hard code `javaFileListFlavor`. Either that or just return `support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)` from `canImport`. – Rangi Keen Oct 29 '18 at 00:46
-5

This works for me. I am using it like this (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
    }
}
hotzen
  • 2,800
  • 1
  • 28
  • 42
  • 7
    (-1) for "The SO-Code-Sample-Button really S****": keep that kind of comment out of the answer, it's useless and clutters what you actually want to explain. If you really have a problem with it, discuss it on the [Meta site](http://meta.stackoverflow.com/), not here. – Gnoupi Jun 01 '10 at 15:59
  • 10
    (-1) for "hey you are looking for solution in Java, let's waste your time and show how it is done in Scala." – Igor S. Jan 06 '14 at 11:47
  • 4
    Scala'ists are looking for the typical java-swing questions as well because we're the same ecosystem after all and this answer is not meant for the OP but for the typical stackoverflow-googler... whatever, hope to haved wasted hours of your previous time. – hotzen Jan 09 '14 at 10:15