How can I use Drag-and-Drop in Swing to get file path?
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?
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();
}
}
});
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
....
}
}
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
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());
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
}
}
精彩评论