将文件从操作系统拖放到 Java 应用程序 (Swing)
Posted
技术标签:
【中文标题】将文件从操作系统拖放到 Java 应用程序 (Swing)【英文标题】:Drag'n drop files from the OS to Java application (Swing) 【发布时间】:2012-02-29 19:58:20 【问题描述】:首先让我说我一直在阅读drag'n drop tutorial 以及关于 SO 提出的类似问题,但不幸的是,我对这件事变得更加困惑。我想要实现的目标相对简单,所以我很惊讶它已经给我带来了这么多麻烦。我正在编写一个小型实用程序应用程序,它将一堆结果文件(自定义定义的 xml 类型)合并到一个大的制表符分隔的文本文件中。大多数功能已经编码,但是我想为它制作一个像样的 GUI。
我想要的是能够以一种优雅而优雅的方式将文件拖放到组件中(例如JTextArea
)(阅读:不是完整路径,而是一个小图标和名称)。我也希望能够提供JFileChooser
来浏览文件。然后我将依次解析文件以生成我想要的矩阵。
到目前为止,我了解到该框架已经存在,但是任何其他功能都需要自定义构建。我在 Netbeans 中创建了一个测试 GUI,并试图将一堆文件拖到 JTextArea
上,但它们显示为文件路径,并且不可否认它看起来非常难看。
我非常感谢有关如何以简洁的方式解决(或澄清)此问题的任何提示和指导。请注意,我确实打算在多个不同的操作系统(Mac、Win 和 Linux)上使用该软件。
编辑:到目前为止,我的代码基于 Sun 教程中的一个示例,如下所示
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.*;
import java.io.*;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.*;
public class ConsolidatorDemo extends JPanel implements ActionListener
private static final long serialVersionUID = -4487732343062917781L;
JFileChooser fc;
JButton clear;
JTextArea dropZone, console;
JSplitPane childSplitPane, parentSplitPane;
PrintStream ps;
public ConsolidatorDemo()
super(new BorderLayout());
fc = new JFileChooser();;
fc.setMultiSelectionEnabled(true);
fc.setDragEnabled(true);
fc.setControlButtonsAreShown(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
JPanel fcPanel = new JPanel(new BorderLayout());
fcPanel.add(fc, BorderLayout.CENTER);
clear = new JButton("Clear All");
clear.addActionListener(this);
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
buttonPanel.add(clear, BorderLayout.LINE_END);
JPanel leftUpperPanel = new JPanel(new BorderLayout());
leftUpperPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
leftUpperPanel.add(fcPanel, BorderLayout.CENTER);
leftUpperPanel.add(buttonPanel, BorderLayout.PAGE_END);
JScrollPane leftLowerPanel = new javax.swing.JScrollPane();
leftLowerPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
dropZone = new JTextArea();
dropZone.setColumns(20);
dropZone.setLineWrap(true);
dropZone.setRows(5);
dropZone.setDragEnabled(true);
dropZone.setDropMode(javax.swing.DropMode.INSERT);
dropZone.setBorder(new TitledBorder("Selected files/folders"));
leftLowerPanel.setViewportView(dropZone);
childSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
leftUpperPanel, leftLowerPanel);
childSplitPane.setDividerLocation(400);
childSplitPane.setPreferredSize(new Dimension(480, 650));
console = new JTextArea();
console.setColumns(40);
console.setLineWrap(true);
console.setBorder(new TitledBorder("Console"));
parentSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
childSplitPane, console);
parentSplitPane.setDividerLocation(480);
parentSplitPane.setPreferredSize(new Dimension(800, 650));
add(parentSplitPane, BorderLayout.CENTER);
public void setDefaultButton()
getRootPane().setDefaultButton(clear);
public void actionPerformed(ActionEvent e)
if (e.getSource() == clear)
dropZone.setText("");
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI()
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
try
//UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaBlackStarLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
if ("Nimbus".equals(info.getName()))
UIManager.setLookAndFeel(info.getClassName());
break;
catch (Exception e)
e.printStackTrace();
//Create and set up the window.
JFrame frame = new JFrame("Consolidator!");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Create and set up the menu bar and content pane.
ConsolidatorDemo demo = new ConsolidatorDemo();
demo.setOpaque(true); //content panes must be opaque
frame.setContentPane(demo);
//Display the window.
frame.pack();
frame.setVisible(true);
demo.setDefaultButton();
public static void main(String[] args)
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable()
public void run()
createAndShowGUI();
);
【问题讨论】:
嗯,是的,也许我没有把问题说清楚; “如何在我的 GUI 中添加拖放功能,其中拖动的文件用它们的文件名和一个小图标很好地表示(系统图标可以,我无意设计自定义图标)?”我已经知道 DnD 文件用它们的路径表示(我相信这几乎是默认行为)。 感谢提醒,代码已添加。 为什么使用 textArea 作为 dropZone?我会使用带有自定义渲染器的列表(显示 FileSystemView 提供的图标/显示名称) @kleopatra:我认为这是最简单的情况。不确定您所说的带有自定义渲染器的列表是什么意思,如果您能举一个简短的例子,我将不胜感激。 【参考方案1】:这里有一个快速的 sn-p 将实际文件导入 JList(而不是将其字符串表示导入文本组件)并使用自定义渲染器很好地呈现它。它改编自 BasicDnD(在教程中):
fileDropper = new JList(new DefaultListModel());
fileDropper.setDragEnabled(true);
leftLowerPanel.setViewportView(fileDropper);
TransferHandler handler = new TransferHandler()
@Override
public boolean canImport(TransferHandler.TransferSupport info)
// we only import FileList
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
return false;
return true;
@Override
public boolean importData(TransferHandler.TransferSupport info)
if (!info.isDrop())
return false;
// Check for FileList flavor
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
displayDropLocation("List doesn't accept a drop of this type.");
return false;
// Get the fileList that is being dropped.
Transferable t = info.getTransferable();
List<File> data;
try
data = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
catch (Exception e) return false;
DefaultListModel model = (DefaultListModel) fileDropper.getModel();
for (File file : data)
model.addElement(file);
return true;
private void displayDropLocation(String string)
System.out.println(string);
;
fileDropper.setTransferHandler(handler);
fileDropper.setCellRenderer(new DefaultListRenderer(
StringValues.FILE_NAME, IconValues.FILE_ICON));
无法拒绝显示 SwingX 渲染器配置 :-) 在核心 java 中,您可以手动完成,类似于
class MyRenderer extends DefaultListCellRenderer
@Override
public Component getListCellRendererComponent(...)
super.getList...
if (value instanceof File)
setText(FileSystemView.getFileSystemView().getDisplayName(value);
setIcon(FileSystemView.getFileSystemView().getSystemIcon(value);
return this;
【讨论】:
感谢您的示例。我猜MyRenderer
的最后一位应该代替fileDropper.setCellRenderer(new DefaultListRenderer(StringValues.FILE_NAME, IconValues.FILE_ICON));
看到StringValues.FILE_NAME
和IconValues.FILE_ICON
未定义/无法解决?还是我误解了什么?
我将其中一些观点纳入您的 SSCCE(并将其作为答案发布)。【参考方案2】:
这实际上是 kleopatra 的答案1(有一些微不足道的变化,不一定会更好),..带有屏幕截图!
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.io.*;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileSystemView;
import javax.swing.text.*;
import java.util.List;
public class ConsolidatorDemo extends JPanel implements ActionListener
private static final long serialVersionUID = -4487732343062917781L;
JFileChooser fc;
JButton clear;
JTextArea console;
JList dropZone;
DefaultListModel listModel;
JSplitPane childSplitPane, parentSplitPane;
PrintStream ps;
public ConsolidatorDemo()
super(new BorderLayout());
fc = new JFileChooser();;
fc.setMultiSelectionEnabled(true);
fc.setDragEnabled(true);
fc.setControlButtonsAreShown(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
JPanel fcPanel = new JPanel(new BorderLayout());
fcPanel.add(fc, BorderLayout.CENTER);
clear = new JButton("Clear All");
clear.addActionListener(this);
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
buttonPanel.add(clear, BorderLayout.LINE_END);
JPanel leftUpperPanel = new JPanel(new BorderLayout());
leftUpperPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
leftUpperPanel.add(fcPanel, BorderLayout.CENTER);
leftUpperPanel.add(buttonPanel, BorderLayout.PAGE_END);
JScrollPane leftLowerPanel = new javax.swing.JScrollPane();
leftLowerPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
listModel = new DefaultListModel();
dropZone = new JList(listModel);
dropZone.setCellRenderer(new FileCellRenderer());
dropZone.setTransferHandler(new ListTransferHandler(dropZone));
dropZone.setDragEnabled(true);
dropZone.setDropMode(javax.swing.DropMode.INSERT);
dropZone.setBorder(new TitledBorder("Selected files/folders"));
leftLowerPanel.setViewportView(new JScrollPane(dropZone));
childSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
leftUpperPanel, leftLowerPanel);
childSplitPane.setDividerLocation(400);
childSplitPane.setPreferredSize(new Dimension(480, 650));
console = new JTextArea();
console.setColumns(40);
console.setLineWrap(true);
console.setBorder(new TitledBorder("Console"));
parentSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
childSplitPane, console);
parentSplitPane.setDividerLocation(480);
parentSplitPane.setPreferredSize(new Dimension(800, 650));
add(parentSplitPane, BorderLayout.CENTER);
public void setDefaultButton()
getRootPane().setDefaultButton(clear);
public void actionPerformed(ActionEvent e)
if (e.getSource() == clear)
listModel.clear();
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI()
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
try
//UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaBlackStarLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
if ("Nimbus".equals(info.getName()))
UIManager.setLookAndFeel(info.getClassName());
break;
catch (Exception e)
e.printStackTrace();
//Create and set up the window.
JFrame frame = new JFrame("Consolidator!");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Create and set up the menu bar and content pane.
ConsolidatorDemo demo = new ConsolidatorDemo();
demo.setOpaque(true); //content panes must be opaque
frame.setContentPane(demo);
//Display the window.
frame.pack();
frame.setVisible(true);
demo.setDefaultButton();
public static void main(String[] args)
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable()
public void run()
createAndShowGUI();
);
class FileCellRenderer extends DefaultListCellRenderer
public Component getListCellRendererComponent(JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
Component c = super.getListCellRendererComponent(
list,value,index,isSelected,cellHasFocus);
if (c instanceof JLabel && value instanceof File)
JLabel l = (JLabel)c;
File f = (File)value;
l.setIcon(FileSystemView.getFileSystemView().getSystemIcon(f));
l.setText(f.getName());
l.setToolTipText(f.getAbsolutePath());
return c;
class ListTransferHandler extends TransferHandler
private JList list;
ListTransferHandler(JList list)
this.list = list;
@Override
public boolean canImport(TransferHandler.TransferSupport info)
// we only import FileList
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
return false;
return true;
@Override
public boolean importData(TransferHandler.TransferSupport info)
if (!info.isDrop())
return false;
// Check for FileList flavor
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
displayDropLocation("List doesn't accept a drop of this type.");
return false;
// Get the fileList that is being dropped.
Transferable t = info.getTransferable();
List<File> data;
try
data = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
catch (Exception e) return false;
DefaultListModel model = (DefaultListModel) list.getModel();
for (Object file : data)
model.addElement((File)file);
return true;
private void displayDropLocation(String string)
System.out.println(string);
-
当我注意到她已经发布了一个答案时,我正忙着写一个答案。因为我的
TransferHandler
很破,所以我用了她的。虽然我使用了我的列表单元格渲染器版本,但它似乎没有捕捉到她所暗示的微妙之处。在提到 List
时,我也遇到了编译错误(假设编译器假设我的意思是 AWT 列表。我只是在将几个 List<File>
类型语句更改为非泛型后才意识到发生了什么 -我想他们中的大多数现在都改回来了。
【讨论】:
非常感谢 Andrew,它对我来说没有问题。 (当我尝试她的示例时,我已经遇到了List
s 的问题)。我不确定我是否完全理解@kleopatra 的ListCellRenderer
建议。我的意思是我遵循你的版本,但我认为我没有完全掌握她版本的技巧。
大部分是FileSystemView.getFileSystemView().getDisplayName(value)
,我怀疑它可能会产生更好的名称(但尚未测试)。顺便说一句 - 如果你在决定给谁打勾时遇到困难,我建议她回答,因为它是正确的,而且是第一位的。
感谢您的澄清,我尝试了他们两个,看看是否有显着差异。我觉得我从你那里得到的帮助比她多,但我还是会尊重你的建议。
不用担心。很高兴你把事情解决了。 :)
@AndrewThompson 能否请您简要介绍一下,为什么要使用 serialversionUid?【参考方案3】:
这是没有控制台和额外按钮的代码
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.PrintStream;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileSystemView;
public class ConsolidatorDemo extends JPanel implements ActionListener
private static final long serialVersionUID = -4487732343062917781L;
// JFileChooser fc;
JButton clear,compare;
JTextArea fc;
JList dropZone;
DefaultListModel listModel;
JSplitPane childSplitPane, parentSplitPane;
PrintStream ps;
public ConsolidatorDemo()
super(new BorderLayout());
/* fc = new JFileChooser();;
fc.setMultiSelectionEnabled(true);
fc.setDragEnabled(true);
fc.setControlButtonsAreShown(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);*/
fc= new JTextArea();
fc.setText("Rules:\n1. drop old html first.\n2. drop new html.\n3. drop output folder.\n4. click compare button.\n5. Check output in the output.txt file.\nEnd");
fc.setEditable(false);
JPanel fcPanel = new JPanel(new BorderLayout());
fcPanel.add(fc, BorderLayout.CENTER);
compare = new JButton("Compare");
compare.addActionListener(this);
JPanel buttonPanel1 = new JPanel(new BorderLayout());
buttonPanel1.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
buttonPanel1.add(compare, BorderLayout.LINE_END);
clear = new JButton("Clear All");
clear.addActionListener(this);
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
buttonPanel.add(clear, BorderLayout.LINE_END);
JPanel leftUpperPanel = new JPanel(new BorderLayout());
leftUpperPanel.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
leftUpperPanel.add(fcPanel, BorderLayout.CENTER);
leftUpperPanel.add(buttonPanel1, BorderLayout.LINE_END);
leftUpperPanel.add(buttonPanel, BorderLayout.PAGE_END);
JScrollPane leftLowerPanel = new javax.swing.JScrollPane();
leftLowerPanel.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
listModel = new DefaultListModel();
dropZone = new JList(listModel);
dropZone.setCellRenderer(new FileCellRenderer());
dropZone.setTransferHandler(new ListTransferHandler(dropZone));
dropZone.setDragEnabled(true);
dropZone.setDropMode(javax.swing.DropMode.INSERT);
dropZone.setBorder(new TitledBorder("Drag and drop files here"));
leftLowerPanel.setViewportView(new JScrollPane(dropZone));
childSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, leftLowerPanel,leftUpperPanel);
childSplitPane.setDividerLocation(200);//400
childSplitPane.setPreferredSize(new Dimension(300, 400));//480, 650
/*console = new JTextArea();
console.setColumns(40);
console.setLineWrap(true);
console.setBorder(new TitledBorder("Console"));
parentSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
childSplitPane, console);
parentSplitPane.setDividerLocation(480);
parentSplitPane.setPreferredSize(new Dimension(800, 650));*/
add(childSplitPane, BorderLayout.CENTER);
public void setDefaultButton()
getRootPane().setDefaultButton(clear);
public void actionPerformed(ActionEvent e)
if (e.getSource() == clear)
listModel.clear();
else if (e.getSource() == compare)
//our function
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI()
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
try
//UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaBlackStarLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
if ("Nimbus".equals(info.getName()))
UIManager.setLookAndFeel(info.getClassName());
break;
catch (Exception e)
e.printStackTrace();
//Create and set up the window.
JFrame frame = new JFrame("Bill of materials Comparer");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Create and set up the menu bar and content pane.
ConsolidatorDemo demo = new ConsolidatorDemo();
demo.setOpaque(true); //content panes must be opaque
frame.setContentPane(demo);
//Display the window.
frame.pack();
frame.setVisible(true);
demo.setDefaultButton();
public static void main(String[] args)
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable()
public void run()
createAndShowGUI();
);
class FileCellRenderer extends DefaultListCellRenderer
public Component getListCellRendererComponent(JList list,Object value,int index,boolean isSelected,boolean cellHasFocus)
Component c = super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
if (c instanceof JLabel && value instanceof File)
JLabel l = (JLabel)c;
File f = (File)value;
l.setIcon(FileSystemView.getFileSystemView().getSystemIcon(f));
l.setText(f.getName());
//l.setText(f.getAbsolutePath());
l.setToolTipText(f.getAbsolutePath());
return c;
class ListTransferHandler extends TransferHandler
private JList list;
ListTransferHandler(JList list)
this.list = list;
@Override
public boolean canImport(TransferHandler.TransferSupport info)
// we only import FileList
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
return false;
return true;
@Override
public boolean importData(TransferHandler.TransferSupport info)
if (!info.isDrop())
return false;
// Check for FileList flavor
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
displayDropLocation("List doesn't accept a drop of this type.");
return false;
// Get the fileList that is being dropped.
Transferable t = info.getTransferable();
List<File> data;
try
data = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
catch (Exception e) return false;
DefaultListModel model = (DefaultListModel) list.getModel();
for (Object file : data)
model.addElement((File)file);
return true;
private void displayDropLocation(String string)
System.out.println(string);
【讨论】:
以上是关于将文件从操作系统拖放到 Java 应用程序 (Swing)的主要内容,如果未能解决你的问题,请参考以下文章
从 Java Swing 应用程序拖放到 Windows 资源管理器
如何将文件从 HTML5 拖放到 Rails 3 应用程序和回形针?