如何在Swing中使用拖放来获取文件path?
在我的swing应用程序中有一个JTextField,它保存选定要使用的文件的文件path。 目前我有一个JFileChooser是用来填充这个值。 不过,我想添加一个用户将文件拖放到此JTextField的function,并将其放入JTextField中,而不是始终使用JFileChooser。
如何才能做到这一点?
首先你应该看看Swing DragDrop的支持 。 之后,对于不同的操作系统有一些小技巧。 一旦你有了事情,你将会处理drop()callback。 在这个callback函数中,你需要检查Transferable的DataFlavor。
对于Windows,您可以只检查DataFlavor.isFlavorJavaFileListType() ,然后获取像这样的数据
List<File> dropppedFiles = (List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor)
对于Linux(也可能是Solaris),DataFlavor有点棘手。 您需要制作自己的DataFlavor,而Transferabletypes将会不同
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 .... } }
有一个示例程序,其中包含一个可用于简化文件和文件夹拖放的类:
http://www.iharder.net/current/java/filedrop/
我testing了这个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
如果你不想花太多时间研究这个相对复杂的主题,而且你正在使用Windows(或使用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(); } } });
我知道这是一个古老的问题,但现在的答案都已经过时了:
- 因为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());
这对我有用。 我正在使用它(斯卡拉):
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 } }