JTable - 拖放
Posted
技术标签:
【中文标题】JTable - 拖放【英文标题】:JTable - drag and drop 【发布时间】:2010-10-17 10:50:01 【问题描述】:好的,这个问题不在我的范围内。我正在尝试在 swing 中实现一个 GUI 小部件,它允许将文件拖放到 JTable 上,并允许拖动 JTable 的行以进行重新排序。想想 VLC 的播放列表或 iTunes 中的播放列表。
我从操作系统(Explorer、Finder 等)中删除文件工作得很好,但是一旦文件进入,我就无法重新安排表格的行。问题是当我向表格添加自定义 TransferHandler 时,从表格中拖动 会立即被杀死。下面是一些示例代码:
import javax.swing.*;
public class TableTest
public static void main (String [] argv)
// setup table data
String [] columns = new String [] "Foo", "Bar", "Baz", "Quux";
String [][] data = new String [][] "A", "B", "C", "D",
"1", "2", "3", "4",
"i", "ii", "iii", "iv";
// create table
JTable table = new JTable(data, columns);
// set up drag and drop
table.setDragEnabled(true);
table.setDropMode(DropMode.INSERT_ROWS);
table.setFillsViewportHeight(true);
TransferHandler dnd = new TransferHandler()
// here be code to handle drops, and one would
// presume drag exporting, too
;
table.setTransferHandler(dnd);
JScrollPane scroll = new JScrollPane(table);
// create and show window
JFrame window = new JFrame();
window.getContentPane().add(scroll);
window.pack();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
按原样运行此代码,您将看到无法在表格上启动拖动。如果您在表格上注释掉对 setTransferHandler() 的调用,则可以进行拖动(即,当我开始拖动表格行时) ,我得到 X'd out 圆形光标说我不能放在那里)。但是一旦为表设置了 TransferHandler,我就不能拖动任何行。问题必须出在 TransferHandler 中,但我已经彻底解决并调试了它,并确定一旦桌子上有 TransferHandler 就永远不会开始拖动。我做错了什么?
【问题讨论】:
多么奇怪。我遇到同样的问题。我试图记住我是否做过任何工作,我有一个支持从和到拖的表格。 【参考方案1】:我遇到了同样的问题,这与您自定义的 TransferHandler 实现无关。当您替换 TransferHandler 时,您还需要获取默认的 DragSource 并告诉它识别拖动手势。您可能还需要实现自己的 Transferable,因为您需要将其传递给 DragGestureEvent.startDrag() 方法。
table.setTransferHandler(new MyTransferHandler());
table.setDragEnabled(true);
DragSource source = DragSource.getDefaultDragSource();
source.createDefaultDragGestureRecognizer(table, DnDConstants.ACTION_COPY, new DragGestureListener()
@Override
public void dragGestureRecognized(DragGestureEvent dge)
//grab the selected files from the table model
ArrayList<File> files = new ArrayList<File>();
for (int row : table.getSelectedRows())
files.add((File) dm.getValueAt(row, 1));
//FileTransferable is a custom Transferable implementation
Transferable transferable = new FileTransferable(files);
//and this is the magic right here
dge.startDrag(null,transferable);
);
【讨论】:
如果我省略了前两行 -setTransferHandler
和 setDragEnabled
,对我有用。事实上,启用拖动设置为我产生了InvalidDnDOperationException: Drag and drop in progress
。同样,我假设dm
指的是table.getModel()
。
完美无瑕。与我之前的海报相同的评论,省略前两行。【参考方案2】:
您似乎没有正确使用 TransferHandler。尝试通读教程here。
请参阅 TransferHandler 文档here。空的构造函数看起来不像是要在 TransferHandler 的子类之外使用。
而且您没有实现 Swing 组件提供的标准 TransferHandler 中提供的任何功能。请参阅 DnD 教程的摘录here(我的粗体字):
注意:如果您将自定义 TransferHandler 安装到 Swing 组件上,则会替换默认支持。例如,如果您将 JTextField 的 TransferHandler 替换为只处理颜色的,您将禁用它支持文本导入和导出的能力。 如果您必须替换默认的 TransferHandler(例如,处理文本的),您将需要重新实现文本导入和导出功能。这不需要像 Swing 提供的那样广泛——它可以像支持 StringFlavor 数据风格一样简单,具体取决于您的应用程序的需要。
【讨论】:
【参考方案3】:我认为问题在于空的 TransferHandler 实际上阻止了 DnD 事件的发生。这里有一个可能相关的示例。
http://www.java2s.com/Code/Java/Swing-JFC/ExtendedDnDDragandDropDemo.htm
【讨论】:
谢谢。这个例子很棒.. :)【参考方案4】:我不想深入了解正在发生的事情,所以我只是将我不感兴趣的方法委托给旧的 TransferHandler。
tree.setDragEnabled(true);
tree.setDropMode(DropMode.XXXX);
tree.setTransferHandler(new MyTransferHandler(tree.getTransferHandler());
从标准设置开始,但将旧的 TransferHandler 传递给您的自定义 TransferHandler。
private class MyTransferHandler extends TransferHandler
private TransferHandler delegate;
public MyTransferHandler(TransferHandler delegate)
this.delegate = delegate;
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors)
return delegate.canImport(comp, transferFlavors);
public boolean canImport(TransferSupport support)
return true;
protected Transferable createTransferable(JComponent c)
try
Method method = delegate.getClass().getDeclaredMethod("createTransferable", JComponent.class);
method.setAccessible(true);
return (Transferable) method.invoke(delegate, c);
catch (Exception e)
return super.createTransferable(c);
public void exportAsDrag(JComponent comp, InputEvent event, int action)
delegate.exportAsDrag(comp, event, action);
protected void exportDone(JComponent source, Transferable data, int action)
try
Method method = delegate.getClass().getDeclaredMethod("exportDone", JComponent.class, Transferable.class,
int.class);
method.setAccessible(true);
method.invoke(delegate, source, data, action);
catch (Exception e)
super.exportDone(source, data, action);
public int getSourceActions(JComponent c)
return delegate.getSourceActions(c);
public Icon getVisualRepresentation(Transferable t)
return delegate.getVisualRepresentation(t);
public boolean importData(JComponent comp, Transferable t)
return delegate.importData(comp, t);
public boolean importData(TransferHandler.TransferSupport support)
return delegate.importData(support);
一个问题是 createTransferable(JComponent) 和 exportDone(JComponent, Transferable, int) 方法受到保护,因此您需要进行反射才能委托给这些方法。当我没有做这个反射委托时,这个策略就不起作用了。一旦我完成了这个委托,拖放就可以按预期工作,而无需更改 DragSource 或编写新的 Transferable。
【讨论】:
草率的示例代码。 boolean 返回类型 canImport 不返回任何内容,而 void 返回类型 exportDone 则返回。 为什么这么马虎?在问题中,他特别指出他正在尝试将 TransferHandler 添加到表中以支持丢弃。他的全部问题是,当他这样做时,桌子的阻力支撑停止工作。幕后发生的事情是,当他提供自己的 TransferHandler 时,他破坏了桌面上安装的默认设置。这是一个如何从表中委托给原始 TransferHandler 的示例,秘密是使用反射来实现 createTransferable() 和 exportDone()。其他方法可以随意覆盖或委托。以上是关于JTable - 拖放的主要内容,如果未能解决你的问题,请参考以下文章