【问题标题】:How can I use Drag-and-Drop in Swing to get file path?如何在 Swing 中使用拖放来获取文件路径?
【发布时间】:2010-10-23 02:42:53
【问题描述】:

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

如何做到这一点?

【问题讨论】:

    标签: java swing drag-and-drop


    【解决方案1】:

    首先您应该查看Swing DragDrop support。之后,针对不同操作系统的小技巧很少。一旦你把事情搞定,你将处理 ​​drop() 回调。在此回调中,您需要检查 Transferable 的 DataFlavor。

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

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

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

    【讨论】:

    • +1 非常感谢! :) 但是,[谢天谢地] Java7 不需要这个技巧。
    • @Oleg:对于 Java 7,您的意思是 DataFlavor 将是 javaFileListFlavor 而与平台无关?
    • @Adamski 是的,对于 Java 7 JRE,使用 javaFileListFlavor 就足够了(至少在 Windows 和 Linux 上)。
    • 阅读 DragDrop 链接后,我发现此页面帮助我非常有效地支持我的自定义 JComponent 的拖放:docs.oracle.com/javase/tutorial/uiswing/dnd/toplevel.html(我所要做的就是实现处理程序)
    【解决方案2】:

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

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

    【讨论】:

    • 这应该是公认的答案。两个 cmets:首先,您应该在接受 drop 之前检查传输数据的类型,其次,您应该在完成任何与 drop 相关的动画后调用evt.dropComplete(true),否则即使放下工作。
    【解决方案3】:

    有一个示例程序,其中包含一个可用于促进文件和文件夹拖放的类:

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

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

    要使用它,您可以在代码中添加如下内容:

      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
    

    【讨论】:

    • @Joe - 来自网站:“可以将任何 java.awt.Component 拖放到上面,但只有 javax.swing.JComponents 会指示带有更改边框的拖放事件。” - 鉴于 JTable 是一个 JComponent,我会说,是的,它完全可以使用它。
    • 很有趣,现在又开始工作了。一定是由于最新的 windows 更新
    【解决方案4】:

    我知道这是一个老问题,但目前的答案都有些过时了:

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

    这适用于 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;
        }
    }
    

    在任何组件上使用它

    myComponent.setTransferHandler(new FileDropHandler());
    

    【讨论】:

    • 在我的 Mac 上用 OS "High Sierra" 测试过,完美运行!!
    • canImport 中迭代数据风格,检查isFlavorJavaFileListType 以获得支持的风格,但在importData 中,您使用getTransferData(DataFlavor.javaFileListFlavor),而不是迭代所有风格。为什么不在canImport 中使用support.isDataFlavorSupported(DataFlavor.javaFileListFlavor) 或在importData 中迭代并执行相同的检查?
    • @RangiKeen 迭代和使用isFlavorJavaFileListType() 更容易 - 使用 Stream API 的单线,不需要 try-catch 块。在importData() 中,只有getTransferData() 才能获得Files。
    • @ABika 但是为什么假设从isFlavorJavaFileListType 返回true 的唯一风味是DataFlavor.javaFileListFlavor?当DataFlavor.javaFileListFlavor 没有传输数据时,您签入canImport 是否有可能返回true?我希望如果您迭代以在canImport 中找到受支持的风格,您还将迭代以在importData 中找到相同的风格,然后使用它来获取传输数据而不是硬代码javaFileListFlavor。要么从canImport返回support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
    【解决方案5】:

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

    【讨论】:

    • (-1) for "The SO-Code-Sample-Button really S****":不要在答案中添加那种评论,它没有用,而且会混淆你真正想要解释的内容.如果你真的有问题,请在Meta site 上讨论,而不是在这里。
    • (-1) 表示“嘿,您正在寻找 Java 中的解决方案,让我们浪费您的时间并展示它是如何在 Scala 中完成的。”
    • Scala'ists 也在寻找典型的 java-swing 问题,因为我们毕竟是同一个生态系统,这个答案不是针对 OP 而是针对典型的 stackoverflow-googler...无论如何,希望浪费你之前的时间。
    猜你喜欢
    • 2021-02-07
    • 2016-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-26
    • 2012-03-28
    • 1970-01-01
    相关资源
    最近更新 更多