我的Swing应用程序中有一个JTextField
,用于保存所选文件的文件路径。目前我使用JFileChooser
来填充该值。但是,我希望用户能够拖放文件到这个JTextField
上,并将该文件的文件路径放入JTextField
中,而不总是使用JFileChooser
。
如何实现这一点?
我的Swing应用程序中有一个JTextField
,用于保存所选文件的文件路径。目前我使用JFileChooser
来填充该值。但是,我希望用户能够拖放文件到这个JTextField
上,并将该文件的文件路径放入JTextField
中,而不总是使用JFileChooser
。
如何实现这一点?
如果您不想花费太多时间研究这个比较复杂的主题,并且您使用的是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();
}
}
});
首先,你应该查看Swing DragDrop支持。之后,对于不同的操作系统,有一些小技巧。一旦你开始工作,就会处理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
....
}
}
有一个示例程序,其中包含一个可用于简化文件和文件夹拖放的类:
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
我知道这是一个老问题,但目前的回答都有点过时:
这在 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());
canImport
中,您会遍历数据类型以检查是否支持 isFlavorJavaFileListType
,但在 importData
中,您使用 getTransferData(DataFlavor.javaFileListFlavor)
而不是遍历所有的数据类型。为什么不在 canImport
中使用 support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
或者在 importData
中进行迭代并执行相同的检查呢? - Rangi KeenisFlavorJavaFileListType()
更容易 - 使用 Stream API 可以实现一行代码,而且不需要 try-catch 块。在 importData()
中,只有通过 getTransferData()
才能获取文件。 - ABikaisFlavorJavaFileListType
返回的唯一类型是DataFlavor.javaFileListFlavor
呢?在没有传输数据的情况下,您在canImport
中的检查是否会返回true?我期望如果您在canImport
中迭代以查找支持的类型,那么您也会在importData
中迭代以查找相同的类型,然后使用它来获取传输数据,而不是硬编码javaFileListFlavor
。或者只需从canImport
返回support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
。 - Rangi Keen这对我很有效。我正在像这样使用它(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
}
}
evt.dropComplete(true)
来完成拖放,否则,尽管拖放起作用,但对用户来说看起来很奇怪。 - Christopher Schultz