Java 如何在 Swing 中使用拖放来获取文件路径?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/811248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:44:41  来源:igfitidea点击:

How can I use Drag-and-Drop in Swing to get file path?

javaswingdrag-and-drop

提问by Ascalonian

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.

我的 Swing 应用程序中有一个 JTextField,用于保存选择使用的文件的文件路径。目前我有一个用于填充此值的 JFileChooser。但是,我想为用户添加将文件拖放到此 JTextField 上的功能,并将该文件的文件路径放入 JTextField 中,而不是始终使用 JFileChooser。

How can this be done?

如何才能做到这一点?

采纳答案by basszero

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.

首先,您应该查看Swing DragDrop 支持。之后,针对不同操作系统的一些小技巧。一旦事情进展顺利,您将处理 drop() 回调。在此回调中,您需要检查 Transferable 的 DataFlavor。

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

对于 Windows,您只需检查DataFlavor.isFlavorJavaFileListType()然后像这样获取数据

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

对于 Linux(可能还有 Solaris),DataFlavor 有点棘手。您需要制作自己的 DataFlavor 并且 Transferable 类型会有所不同

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
       ....
    }
}

回答by hotzen

This works for me. I am using it like this (scala):

这对我有用。我像这样使用它(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
    }
}

回答by Mr Ed

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/

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.

我在 Windows 7 和 Ubuntu 10.10 上对此进行了测试,它似乎在两种环境中都运行良好。

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

回答by rustyx

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 JTextAreaas a drop target:

如果您不想花太多时间研究这个相对复杂的主题,并且您使用的是 Java 7 或更高版本,这里有一个快速示例,说明如何接受以 aJTextArea作为放置目标的已放置文件:

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();
        }
    }
});

回答by ABika

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
  • 从 JDK 1.6 开始,类“TransferHandler”应该与新的(覆盖的)方法一起使用
  • 对 Linux 的支持变得更好,无需自定义处理

This works on Linux (KDE5) and Windows 7:

这适用于 Linux (KDE5) 和 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());